cdesktopenv/cde/programs/dticon/image.c

569 lines
18 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $XConsortium: image.c /main/4 1995/11/02 14:05:39 rswiston $ */
/*********************************************************************
* (c) Copyright 1993, 1994 Hewlett-Packard Company
* (c) Copyright 1993, 1994 International Business Machines Corp.
* (c) Copyright 1993, 1994 Sun Microsystems, Inc.
* (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
* Novell, Inc.
**********************************************************************/
/******************************************************************************
** Program: dticon
**
** Description: X11-based multi-color icon editor
**
** File: Image.c, containing the following subroutines/functions:
** Mirror_Image()
** Block_Rotate()
** Scale_Image()
** Flood_Region()
** Flood_Fill()
**
******************************************************************************
**
** Copyright Hewlett-Packard Company, 1990, 1991, 1992.
** All rights are reserved. Copying or reproduction of this program,
** except for archival purposes, is prohibited without prior written
** consent of Hewlett-Packard Company.
**
** Hewlett-Packard makes no representations about the suitibility of this
** software for any purpose. It is provided "as is" without express or
** implied warranty.
**
******************************************************************************/
#include <stdio.h>
#include "externals.h"
#include "utils.h"
#include "process.h"
#include "image.h"
static int flood_min_x, flood_min_y, flood_max_x, flood_max_y;
static void Set_FloodLimits(int x, int y);
static void Flood_Fill(
XImage *color_image,
XImage *mono_image,
int x,
int y,
int width,
int height,
unsigned long new_pixel,
unsigned long new_mono);
/***************************************************************************
* *
* Routine: Mirror_Image *
* *
* Purpose: Pick "horizontal" or "vertical" from a submenu. Then pick *
* the rectangle to be flopped. Create a mirror image (either *
* top-to-bottom or left-to-right) and prompt the user for *
* placement of the result. *
* *
*X11***********************************************************************/
int
Mirror_Image(
int orientation )
{
XImage *new_image, *old_image, *new_mono, *old_mono;
unsigned long n;
int i, j;
#ifdef DEBUG
if (debug)
stat_out("Entering Mirror_Image\n");
#endif
if (!Selected)
return (False);
/*--- get src. & dst. images from both color and monochrome icons ---*/
old_image = XGetImage(dpy, color_icon, select_box.x, select_box.y,
select_box.width, select_box.height, AllPlanes, format);
if (old_image == NULL)
return (False);
new_image = XGetImage(dpy, color_icon, select_box.x, select_box.y,
select_box.width, select_box.height, AllPlanes, format);
if (new_image == NULL) {
XDestroyImage(old_image);
return (False);
}
old_mono = XGetImage(dpy, mono_icon, select_box.x, select_box.y,
select_box.width, select_box.height, AllPlanes, format);
if (old_mono == NULL) {
XDestroyImage(old_image);
XDestroyImage(new_image);
return (False);
}
new_mono = XGetImage(dpy, mono_icon, select_box.x, select_box.y,
select_box.width, select_box.height, AllPlanes, format);
if (new_mono == NULL) {
XDestroyImage(old_image);
XDestroyImage(new_image);
XDestroyImage(old_mono);
return (False);
}
#ifdef DEBUG
if (debug)
stat_out(" - got the images\n");
#endif
for (i=0; i<(int)select_box.width; i++)
for (j=0; j<(int)select_box.height; j++) {
if (orientation == VERTICAL) {
n = XGetPixel(old_image, i, j);
XPutPixel(new_image, i, (select_box.height-1)-j, n);
n = XGetPixel(old_mono, i, j);
XPutPixel(new_mono, i, (select_box.height-1)-j, n);
}
else {
n = XGetPixel(old_image, i, j);
XPutPixel(new_image, (select_box.width-1)-i, j, n);
n = XGetPixel(old_mono, i, j);
XPutPixel(new_mono, (select_box.width-1)-i, j, n);
}
} /* for(j...) */
XFlush(dpy);
XPutImage(dpy, color_icon, Color_gc, new_image, 0, 0,
select_box.x, select_box.y,
select_box.width, select_box.height);
XPutImage(dpy, XtWindow(iconImage), Color_gc, new_image, 0, 0,
select_box.x, select_box.y,
select_box.width, select_box.height);
XPutImage(dpy, mono_icon, Mono_gc, new_mono, 0, 0,
select_box.x, select_box.y,
select_box.width, select_box.height);
XPutImage(dpy, XtWindow(monoImage), Mono_gc, new_mono, 0, 0,
select_box.x, select_box.y,
select_box.width, select_box.height);
Transfer_Back_Image(select_box.x, select_box.y,
(select_box.x+select_box.width-1),
(select_box.y+select_box.height-1),
FILL);
XDestroyImage(new_image);
XDestroyImage(old_image);
XDestroyImage(new_mono);
XDestroyImage(old_mono);
#ifdef DEBUG
if (debug)
stat_out("Leaving Mirror_Image\n");
#endif
return (True);
}
/***************************************************************************
* *
* Routine: Block_Rotate *
* *
* Purpose: Given source and destination pixmaps of the correct size, *
* and the type of rotation to do, do a block rotation (90 *
* degrees clockwise or counterclockwise) from the source to *
* the destination. *
* *
*X11***********************************************************************/
int
Block_Rotate(
XImage *src_image,
XImage *dst_image,
int rtype )
{
int i, j, width, height;
unsigned long n;
#ifdef DEBUG
if (debug)
stat_out("Entering Block_Rotate\n");
#endif
width = src_image->width;
height = src_image->height;
switch (rtype) {
case ROTATE_L : for (i=0; i<width; i++)
for (j=0; j<height; j++) {
n = XGetPixel(src_image, i, j);
XPutPixel(dst_image, j, (width-1)-i, n);
}
break;
case ROTATE_R : for (i=0; i<width; i++)
for (j=0; j<height; j++) {
n = XGetPixel(src_image, i, j);
XPutPixel(dst_image, (height-1)-j, i, n);
}
break;
} /* switch */
XFlush(dpy);
#ifdef DEBUG
if (debug)
stat_out("Leaving Block_Rotate\n");
#endif
return (True);
}
/***************************************************************************
* *
* Routine: Scale_Image *
* *
* Purpose: Given a scr. and dst. XImage pair, scale the src. image to *
* the size of the dst. image. *
* *
*X11***********************************************************************/
void
Scale_Image( void )
{
XImage *old_img, *old_mono;
int old_x, old_y, new_x, new_y;
int old_width, old_height, new_width, new_height;
int min_x, min_y, max_x, max_y;
#ifdef DEBUG
if (debug)
stat_out("Entering Scale_Image\n");
#endif
min_x = min(ix, last_ix);
min_y = min(iy, last_iy);
max_x = max(ix, last_ix);
max_y = max(iy, last_iy);
/*** make sure all four points are on the tablet ***/
if (min_x < 0)
min_x = 0;
if (min_y < 0)
min_y = 0;
if ((max_x) >= icon_width)
max_x = icon_width-1;
if ((max_y) >= icon_height)
max_y = icon_height-1;
old_img = XGetImage(dpy, color_icon, select_box.x, select_box.y,
select_box.width, select_box.height,
AllPlanes, format);
old_mono = XGetImage(dpy, mono_icon, select_box.x, select_box.y,
select_box.width, select_box.height,
AllPlanes, format);
Scale = XGetImage(dpy, color_icon, min_x, min_y,
(max_x-min_x+1), (max_y-min_y+1), AllPlanes, format);
Scale_mono = XGetImage(dpy, mono_icon, min_x, min_y,
(max_x-min_x+1), (max_y-min_y+1), AllPlanes, format);
old_width = old_img->width;
old_height = old_img->height;
new_width = Scale->width;
new_height = Scale->height;
for (new_y=0; new_y<new_height; new_y++) {
old_y = (old_height * new_y) / new_height;
for (new_x=0; new_x<new_width; new_x++) {
old_x = (old_width * new_x) / new_width;
XPutPixel(Scale, new_x, new_y, XGetPixel(old_img, old_x, old_y));
XPutPixel(Scale_mono, new_x, new_y, XGetPixel(old_mono, old_x, old_y));
}
}
XDestroyImage(old_img);
XDestroyImage(old_mono);
#ifdef DEBUG
if (debug)
stat_out("Leaving Scale_Image\n");
#endif
}
/***************************************************************************
* *
* Routine: Flood_Region *
* *
* Purpose: Pick the (rectangular) region to be flooded by a new color. *
* Then pick an old color (pixel) in that region to be replaced *
* by the new color (left button = current foreground, right *
* button = current background). *
* *
*X11***********************************************************************/
int
Flood_Region(
int flood_x,
int flood_y )
{
XImage *ImagePix, *MonoPix;
unsigned long new_pixel, new_mono;
#ifdef DEBUG
int i, j;
unsigned long old_pixel;
if (debug)
stat_out("Entering Flood_Region\n");
#endif
/*--- get the image from the (adjusted) box ---*/
ImagePix = XGetImage(dpy, color_icon, 0, 0, icon_width, icon_height,
AllPlanes, format);
if (ImagePix == NULL)
return (False);
MonoPix = XGetImage(dpy, mono_icon, 0, 0, icon_width, icon_height,
AllPlanes, format);
if (MonoPix == NULL)
return (False);
#ifdef DEBUG
if (debug)
stat_out(" - got the image\n");
#endif
if (ColorBlock == STATIC_COLOR) {
new_pixel = StaticPen[CurrentColor];
new_mono = StaticMono[CurrentColor];
}
else {
new_pixel = DynamicPen[CurrentColor];
new_mono = DynamicMono[CurrentColor];
}
#ifdef DEBUG
if (debug) {
for (i=0; i<icon_width; i++)
for (j=0; j<icon_height; j++) {
old_pixel = XGetPixel(ImagePix, i, j);
if ((old_pixel < 0) || (old_pixel > 255))
stat_out(" BAD PIXEL VALUE (%d) AT [%d,%d]\n", old_pixel, i, j);
}
stat_out(" SUCCESSFULLY accessed each pixel in the image\n");
}
#endif
flood_min_x = icon_width;
flood_min_y = icon_height;
flood_max_x = 0;
flood_max_y = 0;
Flood_Fill(ImagePix, MonoPix, flood_x, flood_y, ImagePix->width,
ImagePix->height, new_pixel, new_mono);
XFlush(dpy);
Backup_Icons();
XPutImage(dpy, color_icon, Color_gc, ImagePix, 0, 0, 0, 0,
icon_width, icon_height);
XPutImage(dpy, XtWindow(iconImage), Color_gc, ImagePix,
0, 0, 0, 0, icon_width, icon_height);
XPutImage(dpy, mono_icon, Mono_gc, MonoPix, 0, 0, 0, 0,
icon_width, icon_height);
XPutImage(dpy, XtWindow(monoImage), Mono_gc, MonoPix,
0, 0, 0, 0, icon_width, icon_height);
Transfer_Back_Image(flood_min_x, flood_min_y,
flood_max_x, flood_max_y, FILL);
XDestroyImage(ImagePix);
XDestroyImage(MonoPix);
#ifdef DEBUG
if (debug)
stat_out("Leaving Flood_Region\n");
#endif
return (True);
}
/***************************************************************************
* *
* Routine: Set_FloodLimits *
* *
* Purpose: Given the current [x,y] of a pixel about to be modified by *
* a flood-fill operation, compare it's location against the *
* limits of the area already affected by the flood-fill. If *
* the pixel is outside the already modified area, adjust the *
* flood_min_x, flood_min_y, flood_max_x, and flood_min_y, so *
* that the current pixel is within the area defined by those *
* four variables. When the flood-fill is completed, the *
* final values for those four variables will be used (by the *
* Transfer_Back_Image() call in Flood_Region()) to minimize *
* the size of the sub-image tranferred back to the tablet *
* from the color icon pixmap. This process slows down the *
* actual flooding operation, but can significantly speed up *
* the transfer_back operation, so there is a net performance *
* gain (potentially, a large one). *
* *
*X11***********************************************************************/
static void
Set_FloodLimits(
int x,
int y )
{
if (x < flood_min_x)
flood_min_x = x;
if (x > flood_max_x)
flood_max_x = x;
if (y < flood_min_y)
flood_min_y = y;
if (y > flood_max_y)
flood_max_y = y;
}
/***************************************************************************
* *
* Routine: Flood_Fill *
* *
* Purpose: Interatively examine each pixel within a bounded area, *
* replacing the old-colored pixels encountered with *
* new-colored pixels. *
* *
***************************************************************************
* one page seed fill program, 1 channel frame buffer version *
* *
* doesn't read each pixel twice like the BASICFILL algorithm in *
* Alvy Ray Smith, "Tint Fill", SIGGRAPH '79 *
* *
* Paul Heckbert 13 Sept 1982, 28 Jan 1987 *
* PIXAR 415-499-3600 *
* P.O. Box 13719 UUCP: {sun,ucbvax}!pixar!ph *
* San Rafael, CA 94913 ARPA: ph%pixar.uucp@ucbvax.berkeley.edu *
*X11***********************************************************************/
/*
* segment of scan line y for xl<=x<=xr was filled,
* now explore adjacent pixels in scan line y+dy
*/
struct seg {short y, xl, xr, dy;};
/*********************************************************
#define MAX 10000
*********************************************************/
#define MAX 20000 /* max depth of stack */
#define PUSH(Y, XL, XR, DY) \
if (sp<stack+MAX && Y+(DY)>=0 && Y+(DY)<height) \
{sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
#define POP(Y, XL, XR, DY) \
{sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
#ifdef DEBUG
int local_debug=False, p_cnt;
#endif
static void
Flood_Fill(
XImage *color_image,
XImage *mono_image,
int x,
int y,
int width,
int height,
unsigned long new_pixel,
unsigned long new_mono )
{
int l, x1, x2, dy;
unsigned long old_pixel;
struct seg stack[MAX], *sp = stack;
old_pixel = XGetPixel(color_image, x, y); /* read pv at seed point */
if (old_pixel==new_pixel || x<0 || x>width || y<0 || y>height) return;
PUSH(y, x, x, 1); /* needed in some cases */
PUSH(y+1, x, x, -1); /* seed segment (popped 1st) */
#ifdef DEBUG
if (local_debug)
p_cnt = 0;
#endif
while (sp>stack) {
/* pop segment off stack and fill a neighboring scan line */
POP(y, x1, x2, dy);
for (x=x1; x>=0 && XGetPixel(color_image, x, y)==old_pixel; x--)
{
#ifdef DEBUG
if (local_debug) {
stat_out("+[%d,%d] ", x, y);
p_cnt++;
if (p_cnt == 8) {
stat_out("\n");
p_cnt = 0;
}
}
#endif
Set_FloodLimits(x, y);
XPutPixel(color_image, x, y, new_pixel);
XPutPixel(mono_image, x, y, new_mono);
}
if (x>=x1) goto skip;
l = x+1;
if (l<x1) PUSH(y, l, x1-1, -dy); /* leak on left? */
x = x1+1;
do {
for (; x<width && XGetPixel(color_image, x, y)==old_pixel; x++)/**TAG**/
{
#ifdef DEBUG
if (local_debug) {
stat_out("-[%d,%d] ", x, y);
p_cnt++;
if (p_cnt == 8) {
stat_out("\n");
p_cnt = 0;
}
}
#endif
Set_FloodLimits(x, y);
XPutPixel(color_image, x, y, new_pixel);
XPutPixel(mono_image, x, y, new_mono);
}
PUSH(y, l, x-1, dy);
if (x>x2+1) PUSH(y, x2+1, x-1, -dy); /* leak on right? */
skip: for (x++; x<=x2 && XGetPixel(color_image, x, y)!=old_pixel; x++)
{
#ifdef DEBUG
if (local_debug) {
stat_out(" [%d,%d] ", x, y);
p_cnt++;
if (p_cnt == 8) {
stat_out("\n");
p_cnt = 0;
}
}
#endif
}
l = x;
} while (x<=x2);
}
}