/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ /* * $Header: /home/gda/dxcvs/dx/src/exec/dxmods/overlay.c,v 1.3 1999/05/10 15:45:28 gda Exp $ */ #include /*** MODULE: Overlay SHORTDESCRIPTION: overlays one image on top of another using either blending or keying. CATEGORY: Import and Export INPUTS: overlay; image; NULL; overlay image base; image; NULL; base image blend; scalar or field or string or vector ; .5; 0 for base image only; 1 for overlay image only OUTPUTS: combined; image; NULL; combined image FLAGS: BUGS: AUTHOR: David Wood END: ***/ #include #include <_helper_jea.h> #include /* for fabs() */ #include #define MAX_SHAPE 64 /* * Default allowable difference in color values when doing chromakeying. * This number is derived from visual inspection. */ #define DELTA_COLOR .001 /* * Determine whether two colors are the same */ #define COLORMATCH(rgb, red,green,blue) \ ( (fabs((rgb)->r - (red)) <= DELTA_COLOR) && \ (fabs((rgb)->g - (green)) <= DELTA_COLOR) && \ (fabs((rgb)->b - (blue)) <= DELTA_COLOR) ) /* * Supported operations. */ #define BLENDING 0x1 /* Constant blend over images */ #define MATTE 0x2 /* Per pixel blend over images */ #define CHROMAKEY 0x3 /* Only overlay when base==color */ /* * Structure used to pass arguments to tasks. * If you add a new structure member, make sure it gets copied to * largs (as appropriate) in create_overlay_tasks(). */ struct overlay_args { /* Input args to overlay_task */ Field base, overlay; /* The 2 images to overlay */ Field combined; /* The output image to build */ int op; /* How do we do the overlay */ union arg3 { /* 3rd argument to overlay */ float blend; /* Field blending coefficient */ Field matte; /* Blend matte */ RGBColor chromakey; /* Color to key on in overlay */ } u; }; typedef enum pixtype { const_ubyte_rgb, const_float_rgb, const_mapped, ubyte_rgb, float_rgb, mapped } Pixtype; static Error blend_images(Pixtype, Pointer, RGBColor*, Pixtype, Pointer, RGBColor*, Pixtype, Pointer, int, int, float); static Error matte_images(Pixtype, Pointer, RGBColor*, Pixtype, Pointer, RGBColor*, Pixtype, Pointer, int, int, float *); static Error key_images(Pixtype, Pointer, RGBColor*, Pixtype, Pointer, RGBColor*, Pixtype, Pointer, int, int, RGBColor *); static Error valid_image_object(Object image); static Error create_overlay_tasks(struct overlay_args *); static Error overlay_task(Pointer); static Error valid_blend_field(Field matte); static Error compatible_grids(Field, Field, char *, char *); static Error getImagePixelData(Field, Pixtype *, Pointer *, RGBColor **); /* * Overlay two images using either of 2 methods depending on the type of * the 3rd argument. If the argument is of type... * * scalar or Field ) Blend the two images such that * o = base * (1-blend) + overlay * blend * where 'blend' is * scalar ) a float constant * field ) a field of float "data" constants * that map 1:1 onto both images. * * vector or string) specifies a color to use as the key when doing * chromakeying, so that * if (base[i] == colorkey) * o[i] = overlay[i] * else * o[i] = base[i] * This is comparable to creating a blend field that * has value 1 where base[i] = colorkey and value 0 * elsewhere. * * The default (when the 3rd argument is not present) is to overlay * the two images as if a single float blend value of .5 had been * provided. * * Partitioned fields are handled properly. * * Currently, the input images (and the blend field) must have the * same positions. * * */ int m_Overlay ( Object *in, Object *out ) { #define I_overlay in[0] #define I_base in[1] #define I_blend in[2] Object combined = NULL; char *colorstr; struct overlay_args args; if ( !I_overlay ) ErrorGotoPlus1 ( ERROR_MISSING_DATA, "#10000", "'overlay'" ); if ( !I_base ) ErrorGotoPlus1 ( ERROR_MISSING_DATA, "#10000", "'base'" ); if (!I_blend) { args.u.blend = 0.5; /* Default is to do a 50/50 blend */ args.op = BLENDING; } /* If it's a string, then it is a color to key on */ else if (DXExtractString(I_blend, &colorstr)) { /* convert from string to DXRGB */ if (!(DXColorNameToRGB(colorstr, &args.u.chromakey))) ErrorGotoPlus1( ERROR_INVALID_DATA, "#11760",colorstr ); args.op = CHROMAKEY; } /* If it's an DXRGB color */ else if (DXExtractParameter(I_blend,TYPE_FLOAT,3,1, (Pointer)&args.u.chromakey)) { args.op = CHROMAKEY; } /* See if it is a float blending value */ else if (DXExtractFloat ( I_blend, &args.u.blend ) ) { if ( ( args.u.blend < 0.0 ) || ( args.u.blend > 1.0 ) ) ErrorGotoPlus3( ERROR_INVALID_DATA, "#10110", "'blend'", 0, 1 ); args.op = BLENDING; } /* See if it is a matte/field */ else if (valid_blend_field((Field)I_blend)) { args.u.matte = (Field)I_blend; args.op = MATTE; } /* Bad data type */ else { ErrorGotoPlus1(ERROR_INVALID_DATA, "#10232","blend"); } /* * Create the structure for the outgoing image and then build * a task group to do the overlay work. */ if (combined = DXCopy(I_base, COPY_STRUCTURE)) { DXCreateTaskGroup(); args.base = (Field)I_base; args.overlay = (Field)I_overlay; args.combined = (Field)combined; if (!create_overlay_tasks(&args)) { DXAbortTaskGroup(); goto error; } else if (!DXExecuteTaskGroup()) goto error; } out[0] = combined; return OK; error: if (combined != I_base) DXDelete(combined); return ERROR; } /* * Create one task for each object with a class of CLASS_FIELD in the * base image (args->base). Called recursively for CLASS_COMPOSITEFIELD * objects. */ static Error create_overlay_tasks(struct overlay_args *args) { Object o, b; int i; struct overlay_args largs; switch (DXGetObjectClass((Object)args->base)) { case CLASS_FIELD: /* * Make sure the overlay image is compatible with the base. */ if (DXGetObjectClass((Object)args->overlay) != CLASS_FIELD) { ErrorGotoPlus2(ERROR_INVALID_DATA, "#12310", "base", "overlay"); } else if (!compatible_grids(args->base, args->overlay, "'base'","'overlay'")) { goto error; } /* * Check to be sure the matte (if being used) is compatible. */ if (args->op == MATTE) { if (DXGetObjectClass((Object)args->u.matte) != CLASS_FIELD) { ErrorGotoPlus2(ERROR_INVALID_DATA,"#12315","blend","matte"); } else if (!compatible_grids(args->base, args->u.matte, "'base'","'blend'")) goto error; } /* * Queue up the work. */ if (!DXAddTask(overlay_task, (Pointer)args, sizeof(*args), 0.0)) goto error; break; case CLASS_GROUP: /* Image must be a composite field */ /* * Make sure the input base image is luking good. */ if (DXGetGroupClass((Group)args->base) != CLASS_COMPOSITEFIELD) ErrorGotoPlus1(ERROR_INVALID_DATA, "#12320", "base"); /* * Check to be sure that the overlay is the same type. */ if ((DXGetObjectClass((Object)args->overlay) != CLASS_GROUP) || (DXGetGroupClass((Group)args->overlay) != CLASS_COMPOSITEFIELD)) ErrorGotoPlus2(ERROR_INVALID_DATA, "#12315","overlay","image"); /* * Check to be sure the matte (if being used) is compatible. */ if ((args->op == MATTE) && ((DXGetObjectClass((Object)args->u.matte) != CLASS_GROUP) || (DXGetGroupClass((Group)args->u.matte)!=CLASS_COMPOSITEFIELD))) ErrorGotoPlus2(ERROR_INVALID_DATA, "#12315", "blend","matte"); /* * Load largs with the constants over all tasks. */ largs.op = args->op; if (largs.op == BLENDING) largs.u.blend = args->u.blend; else if (largs.op == CHROMAKEY) largs.u.chromakey = args->u.chromakey; /* * Create one task for each member of the inputs. */ for (i=0; (b = DXGetEnumeratedMember((Group)args->base,i,NULL)) && (o = DXGetEnumeratedMember((Group)args->overlay,i,NULL)) ; i++) { largs.overlay = (Field)o; largs.base = (Field)b; largs.combined = (Field)DXGetEnumeratedMember( (Group)args->combined,i,NULL); if (largs.op == MATTE) { largs.u.matte = (Field)DXGetEnumeratedMember( (Group)args->u.matte, i,NULL); if (!largs.u.matte) goto error; } if (!create_overlay_tasks(&largs)) goto error; } break; default: ErrorGotoPlus1(ERROR_INVALID_DATA, "#12320", "base"); } return OK; error: return ERROR; } /* * Overlay two images * args->combined = foo(args->base, args->overlay) * The supplied images (args->[base,overlay,combined]) are all assumed to be * simple images (i.e. not composite fields). */ static Error overlay_task(Pointer ptr) { struct overlay_args *args = (struct overlay_args *)ptr; Field combined = args->combined; Field base = args->base; Field overlay = args->overlay; Array a = NULL, blend; Pointer c_ptr, o_ptr, b_ptr; float *m_ptr; RGBColor *o_map = NULL, *b_map = NULL; Pixtype o_type, b_type, c_type; Type type; Category cat; int height1, width1, height2, width2; int nitems, rank, shape[MAX_SHAPE]; /* * Be sure that the base object is an image */ if (!valid_image_object((Object)base)) ErrorGotoPlus1 ( ERROR_INVALID_DATA, "#12320", "base" ); /* * Be sure that the overlay object is an image */ if (!valid_image_object((Object)overlay)) ErrorGotoPlus1 ( ERROR_INVALID_DATA, "#12320", "overlay" ); /* * Get overlay, base and output image info */ if (! getImagePixelData(overlay, &o_type, &o_ptr, &o_map)) goto error; if (! getImagePixelData(base, &b_type, &b_ptr, &b_map)) goto error; /* * Get and compare sizes of base and overlay */ if (!DXGetImageSize(overlay, &width1, &height1) || !DXGetImageSize(base, &width2, &height2)) DXErrorGoto( ERROR_INVALID_DATA, "#12325"); /* * Make sure the images are the same size */ if ( (height1 != height2) || (width1 != width2) ) { DXSetError ( ERROR_INVALID_DATA, "#12330", height1, width1, height2, width2 ); goto error; } /* * Create a new array of pixels to hold the combined image. */ if (! getenv("DXPIXELTYPE") || !strcmp(getenv("DXPIXELTYPE"), "DXFloat")) a = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 3); else a = DXNewArray(TYPE_UBYTE, CATEGORY_REAL, 1, 3); if (!a || !DXAddArrayData(a, 0, width1*height1, NULL)) goto error; /* * Install the new image (colors) in the output field */ if (!DXSetComponentValue(combined, "colors", (Object)a)) goto error; if (! getImagePixelData(combined, &c_type, &c_ptr, NULL)) goto error; /* * Do the work to overlay the images */ if (args->op == BLENDING) { if (! blend_images(b_type, b_ptr, b_map, o_type, o_ptr, o_map, c_type, c_ptr, height1, width1, args->u.blend)) goto error; } else if (args->op == MATTE) { /* * Make sure the field has the correct structure */ if (!valid_blend_field(args->u.matte)) ErrorGotoPlus1 ( ERROR_INVALID_DATA, "#12335", "blend" ); /* * DXExtract the array of blend values */ blend = (Array)DXGetComponentValue(args->u.matte,"data"); if (!blend) ErrorGotoPlus2 ( ERROR_INVALID_DATA, "#10250", "'blend'", "data"); /* * Check the number, type, category, rank and shape of data provided */ DXGetArrayInfo(blend, &nitems, &type, &cat, &rank, shape); if (nitems != (width1 * height1)) ErrorGotoPlus1 ( ERROR_INVALID_DATA, "#12340","blend" ); if (type != TYPE_FLOAT) ErrorGotoPlus1 ( ERROR_INVALID_DATA, "#10330", "'blend' data" ); if (cat != CATEGORY_REAL) ErrorGotoPlus1(ERROR_INVALID_DATA,"#11150","'blend' data"); if ((rank != 0) && !(rank == 1 && shape[0] == 1)) ErrorGotoPlus1 ( ERROR_INVALID_DATA, "#10081", "'blend' data"); /* * Get a pointer to the real float values */ m_ptr = (float*)DXGetArrayData(blend); if (!m_ptr) ErrorGotoPlus2 ( ERROR_INTERNAL, "#10250", "'blend'","data" ); if (! matte_images(b_type, b_ptr, b_map, o_type, o_ptr, o_map, c_type, c_ptr, height1, width1, m_ptr)) goto error; } else if (args->op == CHROMAKEY) { if (! key_images(b_type, b_ptr, b_map, o_type, o_ptr, o_map, c_type, c_ptr, height1, width1, &args->u.chromakey)) goto error; } else DXErrorGoto ( ERROR_UNEXPECTED, "bad operation" ); /* Need to remove any "direct color map" attribute if present, * since colors are expanded, this attribute does not make sense. * Similarly, colors should not ref a colormap, * and the color map component should be removed. DLG 4/30/97 */ if (!DXSetAttribute((Object)combined,"direct color map", NULL)) goto error; if (!DXSetComponentAttribute((Field)combined,"colors","ref",NULL)) goto error; if (!DXSetComponentValue((Field)combined,"color map",NULL)) goto error; return OK; error: if (a) DXDelete((Object)a); return ERROR; } #define BYTE_TO_FLOAT(a) ((a)*(1.0/255.0)) #define FLOAT_TO_BYTE(a) ((a)*255.0) #define GET_PIXEL(dst, tmp, type, u_p, f_p, map) \ { \ if (type == ubyte_rgb) \ { \ tmp.r = BYTE_TO_FLOAT(*u_p++); \ tmp.g = BYTE_TO_FLOAT(*u_p++); \ tmp.b = BYTE_TO_FLOAT(*u_p++); \ dst = &tmp; \ } \ else if (type == float_rgb) \ { \ dst = f_p++; \ } \ else if (type == mapped) \ { \ dst = map + *u_p++; \ } \ } #define PUT_PIXEL(type, u_p, f_p, res) \ { \ if (type == ubyte_rgb) \ { \ *u_p++ = FLOAT_TO_BYTE((res)->r); \ *u_p++ = FLOAT_TO_BYTE((res)->g); \ *u_p++ = FLOAT_TO_BYTE((res)->b); \ } \ else if (type == float_rgb) \ { \ f_p->r = (res)->r; \ f_p->g = (res)->g; \ f_p->b = (res)->b; \ f_p++; \ } \ } /* * Blend two images such that * dest = (1-blend)*base + blend*overlay; */ static Error blend_images(Pixtype b_type, Pointer b_ptr, RGBColor *b_map, Pixtype o_type, Pointer o_ptr, RGBColor *o_map, Pixtype c_type, Pointer c_ptr, int height, int width, float blend) { float bbar = 1.0 - blend; int i,j,n; RGBColor *fb = (RGBColor *)b_ptr, b_tmp, *b; RGBColor *fo = (RGBColor *)o_ptr, o_tmp, *o; RGBColor *fc = (RGBColor *)c_ptr; ubyte *ub = (ubyte *)b_ptr; ubyte *uo = (ubyte *)o_ptr; ubyte *uc = (ubyte *)c_ptr; if (b_type == const_ubyte_rgb) { b_tmp.r = BYTE_TO_FLOAT(*ub++); b_tmp.g = BYTE_TO_FLOAT(*ub++); b_tmp.b = BYTE_TO_FLOAT(*ub++); b = &b_tmp; } else if (b_type == const_float_rgb) { b = (RGBColor *)b_ptr; } else if (b_type == const_mapped) { b = b_map + *ub; } if (o_type == const_ubyte_rgb) { o_tmp.r = BYTE_TO_FLOAT(*uo++); o_tmp.g = BYTE_TO_FLOAT(*uo++); o_tmp.b = BYTE_TO_FLOAT(*uo++); o = &o_tmp; } else if (o_type == const_float_rgb) { o = (RGBColor *)o_ptr; } else if (o_type == const_mapped) { o = o_map + *uo; } n = height*width; for (i=0; ir * bbar) + (o->r * blend); result.g = (b->g * bbar) + (o->g * blend); result.b = (b->b * bbar) + (o->b * blend); PUT_PIXEL(c_type, uc, fc, &result); } return OK; } /* * Chromakey two images such that * dest->pixel = (base->pixel == key ? overlay->pixel : base->pixel) * for all pixels in base. */ static Error key_images(Pixtype b_type, Pointer b_ptr, RGBColor *b_map, Pixtype o_type, Pointer o_ptr, RGBColor *o_map, Pixtype c_type, Pointer c_ptr, int height, int width, RGBColor *key) { int i,j,n; RGBColor *fb = (RGBColor *)b_ptr, b_tmp, *b; RGBColor *fo = (RGBColor *)o_ptr, o_tmp, *o; RGBColor *fc = (RGBColor *)c_ptr; ubyte *ub = (ubyte *)b_ptr; ubyte *uo = (ubyte *)o_ptr; ubyte *uc = (ubyte *)c_ptr; float red = key->r; float green = key->g; float blue = key->b; if (b_type == const_ubyte_rgb) { b_tmp.r = BYTE_TO_FLOAT(*ub++); b_tmp.g = BYTE_TO_FLOAT(*ub++); b_tmp.b = BYTE_TO_FLOAT(*ub++); b = &b_tmp; } else if (b_type == const_float_rgb) { b = (RGBColor *)b_ptr; } else if (b_type == const_mapped) { b = b_map + *ub; } if (o_type == const_ubyte_rgb) { o_tmp.r = BYTE_TO_FLOAT(*uo++); o_tmp.g = BYTE_TO_FLOAT(*uo++); o_tmp.b = BYTE_TO_FLOAT(*uo++); o = &o_tmp; } else if (o_type == const_float_rgb) { o = (RGBColor *)o_ptr; } else if (o_type == const_mapped) { o = o_map + *uo; } n = height*width; for (i=0; ipixel = base->pixel *(1-matte->data) + overlay->pixel*matte->data) * for all pixels in base. */ static Error matte_images(Pixtype b_type, Pointer b_ptr, RGBColor *b_map, Pixtype o_type, Pointer o_ptr, RGBColor *o_map, Pixtype c_type, Pointer c_ptr, int height, int width, float *blend) { int i,j,n; RGBColor *fb = (RGBColor *)b_ptr, b_tmp, *b; RGBColor *fo = (RGBColor *)o_ptr, o_tmp, *o; RGBColor *fc = (RGBColor *)c_ptr; ubyte *ub = (ubyte *)b_ptr; ubyte *uo = (ubyte *)o_ptr; ubyte *uc = (ubyte *)c_ptr; if (b_type == const_ubyte_rgb) { b_tmp.r = BYTE_TO_FLOAT(*ub++); b_tmp.g = BYTE_TO_FLOAT(*ub++); b_tmp.b = BYTE_TO_FLOAT(*ub++); b = &b_tmp; } else if (b_type == const_float_rgb) { b = (RGBColor *)b_ptr; } else if (b_type == const_mapped) { b = b_map + *ub; } if (o_type == const_ubyte_rgb) { o_tmp.r = BYTE_TO_FLOAT(*uo++); o_tmp.g = BYTE_TO_FLOAT(*uo++); o_tmp.b = BYTE_TO_FLOAT(*uo++); o = &o_tmp; } else if (o_type == const_float_rgb) { o = (RGBColor *)o_ptr; } else if (o_type == const_mapped) { o = o_map + *uo; } n = height*width; for (i=0; i 1.0) /* Must check blends */ ErrorGotoPlus3( ERROR_INVALID_DATA, "#10110", "'blend' field datum", 0, 1 ); GET_PIXEL(b, b_tmp, b_type, ub, fb, b_map); GET_PIXEL(o, o_tmp, o_type, uo, fo, o_map); result.r = (b->r * obar) + (o->r * blnd); result.g = (b->g * obar) + (o->g * blnd); result.b = (b->b * obar) + (o->b * blnd); PUT_PIXEL(c_type, uc, fc, &result); } return OK; error: return ERROR; } /* * Determine if the given object has top level structure that is consistent * with an image. */ static Error valid_image_object(Object image) { switch ( DXGetObjectClass (image) ) { case CLASS_FIELD: return(_dxf_ValidImageField ((Field) image)); case CLASS_GROUP: switch ( DXGetGroupClass ( (Group) image)) { case CLASS_COMPOSITEFIELD: return(OK); } } return(ERROR); } /* * Determine whether the given object represents a valid blending field. */ static Error valid_blend_field(Field matte) { switch ( DXGetObjectClass ((Object)matte) ) { case CLASS_FIELD: return OK; case CLASS_GROUP: switch ( DXGetGroupClass ( (Group) matte)) { case CLASS_COMPOSITEFIELD: return OK; } } return ERROR; } /* * Check two images to be sure they have similar positions. * * Compatible images must have grids that... * 1) are 2 dimensional. * 2) are dependendent on positions (not connections) * 3) have the same number of positions in each dimension * 4) have the same origin. * 5) have the save deltas * * Note: We assume (for error messages) that f1 is the base image and f2 * is the overlay image. * * Returns OK/ERROR and sets error code. */ static Error compatible_grids(Field f1, Field f2, char *f1name, char *f2name) { Array p1, p2; int n1,n2,i; /* Make these larger than they have to be in case the grid is > 2 */ int cnt1[2], cnt2[2]; float origin1[2], origin2[2]; float delta1[2][2], delta2[2][2]; p1 = (Array)DXGetComponentValue(f1, "positions"); if (!p1) { DXSetError(ERROR_BAD_PARAMETER,"#10250",f1name, "positions"); goto error; } p2 = (Array)DXGetComponentValue(f2, "positions"); if (!p2) { DXSetError(ERROR_BAD_PARAMETER,"#10250",f2name, "positions"); goto error; } if (!DXQueryGridPositions(p1, &n1, NULL, NULL, NULL)) ErrorGotoPlus1(ERROR_BAD_PARAMETER, "#10612",f1name); if (!DXQueryGridPositions(p2, &n2, NULL, NULL, NULL)) ErrorGotoPlus1(ERROR_BAD_PARAMETER, "#10612",f2name); if (n1 != 2) ErrorGotoPlus1(ERROR_BAD_PARAMETER,"#10333",f1name); if (n2 != 2) ErrorGotoPlus1(ERROR_BAD_PARAMETER,"#10333",f2name); /* Now that we know n1=n2=2, fill the arrays of information */ if (!DXQueryGridPositions(p1, NULL, cnt1, origin1, delta1[0]) || !DXQueryGridPositions(p2, NULL, cnt2, origin2, delta2[0])) DXErrorGoto(ERROR_UNEXPECTED, "Cannot re-query grid positions"); for (i=0 ; i<2 ; i++) { if (cnt1[i] != cnt2[i]) { DXSetError(ERROR_BAD_PARAMETER,"#12345", i,f2name,cnt2[i],f1name,cnt1[i]); goto error; } if (origin1[i] != origin2[i]) { DXSetError(ERROR_BAD_PARAMETER,"#12345", i,f2name, origin2[i],f1name, origin1[i]); goto error; } if ((delta1[i][0] != delta2[i][0]) || (delta1[i][1] != delta2[i][1])) { DXSetError(ERROR_BAD_PARAMETER, "#12355", i,f2name,delta2[i][0],delta2[i][1], f1name,delta1[i][0],delta1[i][1]); goto error; } } return OK; error: return ERROR; } static Error getImagePixelData(Field image, Pixtype *ptype, Pointer *pixels, RGBColor **map) { Type type; Category cat; int rank, shape[32]; Array a = (Array)DXGetComponentValue(image, "colors"); if (! a) DXErrorGoto(ERROR_INVALID_DATA, "#12325"); DXGetArrayInfo(a, NULL, &type, &cat, &rank, shape); if (type == TYPE_FLOAT) { if (cat != CATEGORY_REAL || rank != 1 || shape[0] != 3) DXErrorGoto(ERROR_INVALID_DATA, "pixel type is invalid"); *ptype = float_rgb; if (map) *map = NULL; } else if (type == TYPE_UBYTE && rank == 1 && shape[0] == 3) { if (cat != CATEGORY_REAL) DXErrorGoto(ERROR_INVALID_DATA, "pixel type is invalid"); *ptype = ubyte_rgb; if (map) *map = NULL; } else if (type == TYPE_UBYTE && (rank == 0 || (rank == 1 && shape[0] == 1))) { Array mapa = (Array)DXGetComponentValue(image, "color map"); if (!map) DXErrorGoto(ERROR_INVALID_DATA, "illegal delayed-color image"); if (cat != CATEGORY_REAL) DXErrorGoto(ERROR_INVALID_DATA, "pixel type is invalid"); if (!mapa) DXErrorGoto(ERROR_INVALID_DATA, "invalid delayed-color image"); *ptype = mapped; if (map) *map = (RGBColor *)DXGetArrayData(mapa); } else DXErrorGoto(ERROR_INVALID_DATA, "pixel type is invalid"); if (DXGetArrayClass(a) == CLASS_CONSTANTARRAY) { *pixels = DXGetConstantArrayData(a); if (*ptype == float_rgb) *ptype = const_float_rgb; else if (*ptype == ubyte_rgb) *ptype = const_ubyte_rgb; else if (*ptype == mapped) *ptype = const_mapped; } else *pixels = DXGetArrayData(a); return OK; error: return ERROR; }