dtcm: Coverity 89670, 88380 and 88201
[oweals/cde.git] / cde / programs / dticon / image.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: image.c /main/4 1995/11/02 14:05:39 rswiston $ */
24 /*********************************************************************
25 *  (c) Copyright 1993, 1994 Hewlett-Packard Company     
26 *  (c) Copyright 1993, 1994 International Business Machines Corp.
27 *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
28 *  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
29 *      Novell, Inc.
30 **********************************************************************/
31 /******************************************************************************
32  **  Program:           dticon
33  **
34  **  Description:       X11-based multi-color icon editor
35  **
36  **  File:              Image.c, containing the following subroutines/functions:
37  **                       Mirror_Image()
38  **                       Block_Rotate()
39  **                       Scale_Image()
40  **                       Flood_Region()
41  **                       Flood_Fill()
42  **
43  ******************************************************************************
44  **
45  **  Copyright Hewlett-Packard Company, 1990, 1991, 1992.
46  **  All rights are reserved.  Copying or reproduction of this program,
47  **  except for archival purposes, is prohibited without prior written
48  **  consent of Hewlett-Packard Company.
49  **
50  **  Hewlett-Packard makes no representations about the suitibility of this
51  **  software for any purpose.  It is provided "as is" without express or
52  **  implied warranty.
53  **
54  ******************************************************************************/
55 #include <stdio.h>
56 #include "externals.h"
57
58 int flood_min_x, flood_min_y, flood_max_x, flood_max_y;
59
60
61 /***************************************************************************
62  *                                                                         *
63  * Routine:   Mirror_Image                                                 *
64  *                                                                         *
65  * Purpose:   Pick "horizontal" or "vertical" from a submenu.  Then pick   *
66  *            the rectangle to be flopped.  Create a mirror image (either  *
67  *            top-to-bottom or left-to-right) and prompt the user for      *
68  *            placement of the result.                                     *
69  *                                                                         *
70  *X11***********************************************************************/
71
72 int
73 Mirror_Image(
74         int orientation )
75 {
76   XRectangle tmp_box;
77   XImage *new_image, *old_image, *new_mono, *old_mono;
78   unsigned long n;
79   int i, j;
80
81 #ifdef DEBUG
82   if (debug)
83     stat_out("Entering Mirror_Image\n");
84 #endif
85
86   if (!Selected)
87     return (False);
88 /*--- get src. & dst. images from both color and monochrome icons ---*/
89
90   old_image = XGetImage(dpy, color_icon, select_box.x, select_box.y,
91                 select_box.width, select_box.height, AllPlanes, format);
92   if (old_image == NULL)
93     return (False);
94   new_image  = XGetImage(dpy, color_icon, select_box.x, select_box.y,
95                 select_box.width, select_box.height, AllPlanes, format);
96   if (new_image == NULL) {
97     XDestroyImage(old_image);
98     return (False);
99    }
100
101   old_mono = XGetImage(dpy, mono_icon, select_box.x, select_box.y,
102                 select_box.width, select_box.height, AllPlanes, format);
103   if (old_mono == NULL) {
104     XDestroyImage(old_image);
105     XDestroyImage(new_image);
106     return (False);
107    }
108   new_mono  = XGetImage(dpy, mono_icon, select_box.x, select_box.y,
109                 select_box.width, select_box.height, AllPlanes, format);
110   if (new_mono == NULL) {
111     XDestroyImage(old_image);
112     XDestroyImage(new_image);
113     XDestroyImage(old_mono);
114     return (False);
115    }
116
117 #ifdef DEBUG
118   if (debug)
119     stat_out("  - got the images\n");
120 #endif
121
122   for (i=0; i<(int)select_box.width; i++)
123     for (j=0; j<(int)select_box.height; j++) {
124       if (orientation == VERTICAL) {
125         n = XGetPixel(old_image, i, j);
126         XPutPixel(new_image, i, (select_box.height-1)-j, n);
127         n = XGetPixel(old_mono, i, j);
128         XPutPixel(new_mono, i, (select_box.height-1)-j, n);
129        }
130       else {
131         n = XGetPixel(old_image, i, j);
132         XPutPixel(new_image, (select_box.width-1)-i, j, n);
133         n = XGetPixel(old_mono, i, j);
134         XPutPixel(new_mono, (select_box.width-1)-i, j, n);
135        }
136      } /* for(j...) */
137   XFlush(dpy);
138
139   XPutImage(dpy, color_icon, Color_gc, new_image, 0, 0,
140                 select_box.x, select_box.y,
141                 select_box.width, select_box.height);
142   XPutImage(dpy, XtWindow(iconImage), Color_gc, new_image, 0, 0,
143                 select_box.x, select_box.y,
144                 select_box.width, select_box.height);
145
146   XPutImage(dpy, mono_icon, Mono_gc, new_mono, 0, 0,
147                 select_box.x, select_box.y,
148                 select_box.width, select_box.height);
149   XPutImage(dpy, XtWindow(monoImage), Mono_gc, new_mono, 0, 0,
150                 select_box.x, select_box.y,
151                 select_box.width, select_box.height);
152   Transfer_Back_Image(select_box.x, select_box.y,
153                         (select_box.x+select_box.width-1),
154                         (select_box.y+select_box.height-1),
155                         FILL);
156
157   XDestroyImage(new_image);
158   XDestroyImage(old_image);
159   XDestroyImage(new_mono);
160   XDestroyImage(old_mono);
161
162 #ifdef DEBUG
163   if (debug)
164     stat_out("Leaving Mirror_Image\n");
165 #endif
166
167   return (True);
168 }
169
170
171 /***************************************************************************
172  *                                                                         *
173  * Routine:   Block_Rotate                                                 *
174  *                                                                         *
175  * Purpose:   Given source and destination pixmaps of the correct size,    *
176  *            and the type of rotation to do, do a block rotation (90      *
177  *            degrees clockwise or counterclockwise) from the source to    *
178  *            the destination.                                             *
179  *                                                                         *
180  *X11***********************************************************************/
181
182 int
183 Block_Rotate(
184         XImage *src_image,
185         XImage *dst_image,
186         int rtype )
187 {
188   int i, j, width, height;
189   unsigned long n;
190
191 #ifdef DEBUG
192   if (debug)
193     stat_out("Entering Block_Rotate\n");
194 #endif
195
196   width  = src_image->width;
197   height = src_image->height;
198
199   switch (rtype) {
200     case ROTATE_L   : for (i=0; i<width; i++)
201                         for (j=0; j<height; j++) {
202                           n = XGetPixel(src_image, i, j);
203                           XPutPixel(dst_image, j, (width-1)-i, n);
204                          }
205                       break;
206     case ROTATE_R   : for (i=0; i<width; i++)
207                         for (j=0; j<height; j++) {
208                           n = XGetPixel(src_image, i, j);
209                           XPutPixel(dst_image, (height-1)-j, i, n);
210                          }
211                       break;
212    } /* switch */
213   XFlush(dpy);
214
215 #ifdef DEBUG
216   if (debug)
217     stat_out("Leaving Block_Rotate\n");
218 #endif
219
220   return (True);
221 }
222
223
224 /***************************************************************************
225  *                                                                         *
226  * Routine:   Scale_Image                                                  *
227  *                                                                         *
228  * Purpose:   Given a scr. and dst. XImage pair, scale the src. image to   *
229  *            the size of the dst. image.                                  *
230  *                                                                         *
231  *X11***********************************************************************/
232
233 void
234 Scale_Image( void )
235 {
236   XImage *old_img, *old_mono;
237   int  old_x, old_y, new_x, new_y;
238   int  old_width, old_height, new_width, new_height;
239   int min_x, min_y, max_x, max_y;
240    
241 #ifdef DEBUG
242   if (debug)
243     stat_out("Entering Scale_Image\n");
244 #endif
245
246   min_x = min(ix, last_ix);
247   min_y = min(iy, last_iy);
248   max_x = max(ix, last_ix);
249   max_y = max(iy, last_iy);
250
251 /*** make sure all four points are on the tablet ***/
252
253   if (min_x < 0)
254     min_x = 0;
255   if (min_y < 0)
256     min_y = 0;
257   if ((max_x) >= icon_width)
258     max_x = icon_width-1;
259   if ((max_y) >= icon_height)
260     max_y = icon_height-1;
261
262   old_img = XGetImage(dpy, color_icon, select_box.x, select_box.y,
263                         select_box.width, select_box.height,
264                         AllPlanes, format);
265   old_mono = XGetImage(dpy, mono_icon, select_box.x, select_box.y,
266                         select_box.width, select_box.height,
267                         AllPlanes, format);
268
269   Scale = XGetImage(dpy, color_icon, min_x, min_y,
270                         (max_x-min_x+1), (max_y-min_y+1), AllPlanes, format);
271   Scale_mono = XGetImage(dpy, mono_icon, min_x, min_y,
272                         (max_x-min_x+1), (max_y-min_y+1), AllPlanes, format);
273
274   old_width  = old_img->width;
275   old_height = old_img->height;
276   new_width  = Scale->width;
277   new_height = Scale->height;
278   for (new_y=0;  new_y<new_height;  new_y++) {
279     old_y = (old_height * new_y) / new_height;
280     for (new_x=0;  new_x<new_width;  new_x++) {
281       old_x = (old_width * new_x) / new_width;
282       XPutPixel(Scale, new_x, new_y, XGetPixel(old_img, old_x, old_y));
283       XPutPixel(Scale_mono, new_x, new_y, XGetPixel(old_mono, old_x, old_y));
284     }
285   }
286
287   XDestroyImage(old_img);
288   XDestroyImage(old_mono);
289
290 #ifdef DEBUG
291   if (debug)
292     stat_out("Leaving Scale_Image\n");
293 #endif
294
295 }
296
297
298 /***************************************************************************
299  *                                                                         *
300  * Routine:   Flood_Region                                                 *
301  *                                                                         *
302  * Purpose:   Pick the (rectangular) region to be flooded by a new color.  *
303  *            Then pick an old color (pixel) in that region to be replaced *
304  *            by the new color (left button = current foreground, right    *
305  *            button = current background).                                *
306  *                                                                         *
307  *X11***********************************************************************/
308
309 int
310 Flood_Region(
311         int flood_x,
312         int flood_y )
313 {
314   XImage *ImagePix, *MonoPix;
315   unsigned long new_pixel, new_mono;
316
317 #ifdef DEBUG
318   int i, j;
319   unsigned long old_pixel;
320
321   if (debug)
322     stat_out("Entering Flood_Region\n");
323 #endif
324
325 /*--- get the image from the (adjusted) box ---*/
326
327   ImagePix = XGetImage(dpy, color_icon, 0, 0, icon_width, icon_height,
328                 AllPlanes, format);
329   if (ImagePix == NULL)
330     return (False);
331   MonoPix = XGetImage(dpy, mono_icon, 0, 0, icon_width, icon_height,
332                 AllPlanes, format);
333   if (MonoPix == NULL)
334     return (False);
335
336 #ifdef DEBUG
337   if (debug)
338     stat_out("  - got the image\n");
339 #endif
340
341   if (ColorBlock == STATIC_COLOR) {
342     new_pixel = StaticPen[CurrentColor];
343     new_mono  = StaticMono[CurrentColor];
344    }
345   else {
346     new_pixel = DynamicPen[CurrentColor];
347     new_mono  = DynamicMono[CurrentColor];
348    }
349
350 #ifdef DEBUG
351   if (debug) {
352     for (i=0; i<icon_width; i++)
353       for (j=0; j<icon_height; j++) {
354         old_pixel = XGetPixel(ImagePix, i, j);
355         if ((old_pixel < 0) || (old_pixel > 255))
356           stat_out("      BAD PIXEL VALUE (%d) AT [%d,%d]\n", old_pixel, i, j);
357        }
358     stat_out("  SUCCESSFULLY accessed each pixel in the image\n");
359    }
360 #endif
361
362   flood_min_x = icon_width;
363   flood_min_y = icon_height;
364   flood_max_x = 0;
365   flood_max_y = 0;
366
367   Flood_Fill(ImagePix, MonoPix, flood_x, flood_y, ImagePix->width,
368         ImagePix->height, new_pixel, new_mono);
369   XFlush(dpy);
370
371   Backup_Icons();
372   XPutImage(dpy, color_icon, Color_gc, ImagePix, 0, 0, 0, 0,
373                 icon_width, icon_height);
374   XPutImage(dpy, XtWindow(iconImage), Color_gc, ImagePix,
375                 0, 0, 0, 0, icon_width, icon_height);
376   XPutImage(dpy, mono_icon, Mono_gc, MonoPix, 0, 0, 0, 0,
377                 icon_width, icon_height);
378   XPutImage(dpy, XtWindow(monoImage), Mono_gc, MonoPix,
379                 0, 0, 0, 0, icon_width, icon_height);
380   Transfer_Back_Image(flood_min_x, flood_min_y,
381                         flood_max_x, flood_max_y, FILL);
382   XDestroyImage(ImagePix);
383   XDestroyImage(MonoPix);
384
385 #ifdef DEBUG
386   if (debug)
387     stat_out("Leaving Flood_Region\n");
388 #endif
389
390   return (True);
391
392 }
393
394
395 /***************************************************************************
396  *                                                                         *
397  * Routine:   Set_FloodLimits                                              *
398  *                                                                         *
399  * Purpose:   Given the current [x,y] of a pixel about to be modified by   *
400  *            a flood-fill operation, compare it's location against the    *
401  *            limits of the area already affected by the flood-fill.  If   *
402  *            the pixel is outside the already modified area, adjust the   *
403  *            flood_min_x, flood_min_y, flood_max_x, and flood_min_y, so   *
404  *            that the current pixel is within the area defined by those   *
405  *            four variables.  When the flood-fill is completed, the       *
406  *            final values for those four variables will be used (by the   *
407  *            Transfer_Back_Image() call in Flood_Region()) to minimize    *
408  *            the size of the sub-image tranferred back to the tablet      *
409  *            from the color icon pixmap.  This process slows down the     *
410  *            actual flooding operation, but can significantly speed up    *
411  *            the transfer_back operation, so there is a net performance   *
412  *            gain (potentially, a large one).                             *
413  *                                                                         *
414  *X11***********************************************************************/
415
416 void
417 Set_FloodLimits(
418         int x,
419         int y )
420 {
421   if (x < flood_min_x)
422     flood_min_x = x;
423   if (x > flood_max_x)
424     flood_max_x = x;
425   if (y < flood_min_y)
426     flood_min_y = y;
427   if (y > flood_max_y)
428     flood_max_y = y;
429 }
430
431
432 /***************************************************************************
433  *                                                                         *
434  * Routine:   Flood_Fill                                                   *
435  *                                                                         *
436  * Purpose:   Interatively examine each pixel within a bounded area,       *
437  *            replacing the old-colored pixels encountered with            *
438  *            new-colored pixels.                                          *
439  *                                                                         *
440  ***************************************************************************
441  * one page seed fill program, 1 channel frame buffer version              *
442  *                                                                         *
443  * doesn't read each pixel twice like the BASICFILL algorithm in           *
444  *      Alvy Ray Smith, "Tint Fill", SIGGRAPH '79                          *
445  *                                                                         *
446  * Paul Heckbert                   13 Sept 1982, 28 Jan 1987               *
447  * PIXAR                           415-499-3600                            *
448  * P.O. Box 13719                  UUCP: {sun,ucbvax}!pixar!ph             *
449  * San Rafael, CA 94913            ARPA: ph%pixar.uucp@ucbvax.berkeley.edu *
450  *X11***********************************************************************/
451
452 /*
453  * segment of scan line y for xl<=x<=xr was filled,
454  * now explore adjacent pixels in scan line y+dy
455  */
456 struct seg {short y, xl, xr, dy;};
457 /*********************************************************
458 #define MAX 10000
459 *********************************************************/
460 #define MAX 20000               /* max depth of stack */
461
462 #define PUSH(Y, XL, XR, DY) \
463     if (sp<stack+MAX && Y+(DY)>=0 && Y+(DY)<height) \
464     {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
465
466 #define POP(Y, XL, XR, DY) \
467     {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
468
469 #ifdef DEBUG
470   int local_debug=False, p_cnt;
471 #endif
472
473 int
474 Flood_Fill(
475         XImage *color_image,
476         XImage *mono_image,
477         int x,
478         int y,
479         int width,
480         int height,
481         unsigned long new_pixel,
482         unsigned long new_mono )
483 {
484   int l, x1, x2, dy;
485   unsigned long old_pixel;
486   struct seg stack[MAX], *sp = stack;
487
488   old_pixel = XGetPixel(color_image, x, y);     /* read pv at seed point */
489   if (old_pixel==new_pixel || x<0 || x>width || y<0 || y>height) return 0;
490   PUSH(y, x, x, 1);                     /* needed in some cases */
491   PUSH(y+1, x, x, -1);          /* seed segment (popped 1st) */
492
493 #ifdef DEBUG
494   if (local_debug)
495     p_cnt = 0;
496 #endif
497   while (sp>stack) {
498     /* pop segment off stack and fill a neighboring scan line */
499     POP(y, x1, x2, dy);
500     for (x=x1; x>=0 && XGetPixel(color_image, x, y)==old_pixel; x--)
501         {
502 #ifdef DEBUG
503   if (local_debug) {
504     stat_out("+[%d,%d] ", x, y);
505     p_cnt++;
506     if (p_cnt == 8) {
507       stat_out("\n");
508       p_cnt = 0;
509      }
510    }
511 #endif
512         Set_FloodLimits(x, y);
513         XPutPixel(color_image, x, y, new_pixel);
514         XPutPixel(mono_image, x, y, new_mono);
515         }
516     if (x>=x1) goto skip;
517     l = x+1;
518     if (l<x1) PUSH(y, l, x1-1, -dy);            /* leak on left? */
519     x = x1+1;
520     do {
521         for (; x<width && XGetPixel(color_image, x, y)==old_pixel; x++)/**TAG**/
522         {
523 #ifdef DEBUG
524   if (local_debug) {
525     stat_out("-[%d,%d] ", x, y);
526     p_cnt++;
527     if (p_cnt == 8) {
528       stat_out("\n");
529       p_cnt = 0;
530      }
531    }
532 #endif
533         Set_FloodLimits(x, y);
534         XPutPixel(color_image, x, y, new_pixel);
535         XPutPixel(mono_image, x, y, new_mono);
536         }
537         PUSH(y, l, x-1, dy);
538         if (x>x2+1) PUSH(y, x2+1, x-1, -dy);    /* leak on right? */
539 skip:   for (x++; x<=x2 && XGetPixel(color_image, x, y)!=old_pixel; x++)
540         {
541 #ifdef DEBUG
542   if (local_debug) {
543     stat_out(" [%d,%d] ", x, y);
544     p_cnt++;
545     if (p_cnt == 8) {
546       stat_out("\n");
547       p_cnt = 0;
548      }
549    }
550 #endif
551         }
552         l = x;
553     } while (x<=x2);
554   }
555 }