/* * image.c -- Image structure manipulating library * (C)Copyright 1999 by Hiroshi Takekawa * This file is part of Enfle. * * Last Modified: Mon Jun 26 18:31:32 2000. * $Id: image.c,v 1.13 2000/06/27 18:19:13 sian Exp $ * * Enfle is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Enfle is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include "enfle.h" #include "expand.h" Image * image_allocate(void) { Image *p; if ((p = (Image *)calloc(1, sizeof(Image))) == NULL) return NULL; #ifdef SHAPE p->pixmask = -1; #endif return p; } int image_calculate_bytes_per_line(int width, ImageType type) { switch (type) { case _MONO: return (width + 7) >> 3; case _GRAY: case _INDEX: return width; case _RGB16: return width * 2; case _RGB24: return width * 3; case _UNKNOWN: default: fprintf(stderr, "image_calculate_bytes_per_line: Unknown image type.\n"); exit(-1); } } Image * image_create(int width, int height, ImageType type) { Image *p; if ((p = image_allocate()) == NULL) return NULL; p->bytes_per_line = image_calculate_bytes_per_line(width, type); p->image_size = p->bytes_per_line * height; if ((p->image = calloc(1, p->image_size)) == NULL) { free(p); return NULL; } p->type = type; p->width = width; p->height = height; return p; } void image_destroy(Image *p) { if (p) { if (p->next != NULL) image_destroy(p->next); if (p->image != NULL) free(p->image); #ifdef SHAPE if (p->mask != NULL) free(p->mask); #endif free(p); } } Image * image_duplicate(Image *p) { Image *q, *qnext = NULL; if (p == NULL) return NULL; if ((q = image_allocate()) == NULL) return NULL; if (p->next != NULL) if ((qnext = image_duplicate(p->next)) == NULL) { image_destroy(q); return NULL; } memcpy(q, p, sizeof(Image)); q->next = qnext; if ((q->image = malloc(p->image_size)) == NULL) { image_destroy(q); return NULL; } memcpy(q->image, p->image, p->image_size); #ifdef SHAPE if (p->mask != NULL) { if ((q->mask = malloc(p->mask_size)) == NULL) { image_destroy(q); return NULL; } memcpy(q->mask, p->mask, p->mask_size); } #endif return q; } int image_expand(Image *p, ImageType to) { unsigned char *d = NULL; switch (p->type) { case _MONO: if (to == _INDEX) d = expand_mono_index(p); break; case _GRAY: if (to == _INDEX) d = expand_gray_index(p); p->type = _INDEX; p->ncolors = 256; return 1; case _INDEX: if (to == _RGB24) d = expand_index_rgb24(p); break; case _RGB16: if (to == _RGB24) d = expand_rgb16_rgb24(p); break; default: break; } if (d == NULL) return 0; free(p->image); p->image = d; p->type = to; p->bytes_per_line = image_calculate_bytes_per_line(p->width, p->type); p->image_size = p->bytes_per_line * p->height; return 1; } void image_expand_auto(Image *p, int depth) { int width = p->width; int height = p->height; switch (p->type) { case _MONO: #ifdef DEBUG fprintf(stderr, "_MONO\n"); #endif if (depth != 1 && !image_expand(p, _INDEX)) { fprintf(stderr, "No enough memory for expand %d bytes\n", width * height); exit(1); } break; case _GRAY: #ifdef DEBUG fprintf(stderr, "_GRAY\n"); #endif if (!image_expand(p, _INDEX)) { fprintf(stderr, "Internal error: Error occurred during image_expand()\n"); exit(1); } break; case _RGB16: #ifdef DEBUG fprintf(stderr, "_RGB16\n"); #endif if (depth != 16 && depth != 15 && !image_expand(p, _RGB24)) { fprintf(stderr, "No enough memory for expand %d bytes\n", width * height * 3); exit(1); } p->ncolors = 65536; break; case _RGB24: #ifdef DEBUG fprintf(stderr, "_RGB24\n"); #endif break; case _INDEX: #ifdef DEBUG fprintf(stderr, "_INDEX\n"); #endif if (depth >= 16) { #ifdef DEBUG fprintf(stderr, "_INDEX -> _RGB24\n"); #endif if (!image_expand(p, _RGB24)) { fprintf(stderr, "No enough memory for expand %d bytes\n", width * height * 3); exit(1); } p->ncolors = 1 << 24; p->transparent.red = p->colormap[p->transparent.index][0]; p->transparent.green = p->colormap[p->transparent.index][1]; p->transparent.blue = p->colormap[p->transparent.index][2]; } break; default: break; } } int image_reduce(Image *p, ImageType to) { int i, bpl; switch (p->type) { case _RGB24: switch (to) { case _RGB16: { unsigned short *d, *dest; unsigned char *s = p->image, r, g, b; /* _RGB24 -> _RGB16 */ bpl = image_calculate_bytes_per_line(p->width, _RGB16); if ((d = dest = malloc(bpl * p->height)) == NULL) return 0; for (i = 0; i < p->width * p->height; i++) { r = *s++; g = *s++; b = *s++; *d++ = ((r << 8) & RED_MASK) | ((g << 3) & GREEN_MASK) | ((b >> 3) & BLUE_MASK); } free(p->image); p->type = _RGB16; p->bytes_per_line = bpl; p->image_size = bpl * p->height; p->image = (unsigned char *)dest; return 1; } case _RGB15: { unsigned short *d, *dest; unsigned char *s = p->image, r, g, b; /* _RGB24 -> _RGB15 */ bpl = image_calculate_bytes_per_line(p->width, _RGB16); if ((d = dest = malloc(bpl * p->height)) == NULL) return 0; for (i = 0; i < p->width * p->height; i++) { r = *s++; g = *s++; b = *s++; *d++ = ((r << 7) & RED_MASK15) | ((g << 2) & GREEN_MASK15) | ((b >> 3) & BLUE_MASK15); } free(p->image); p->type = _RGB16; p->bytes_per_line = bpl; p->image_size = bpl * p->height; p->image = (unsigned char *)dest; return 1; } default: fprintf(stderr, "Unimplemented method: %d to %d\n", p->type, to); } default: break; /* ignored */ } return 0; } /* magnify_generic magnifies only the first image */ int image_magnify_generic(Image *p, int dwidth, int dheight, Interpolate_method method) { unsigned char *d; if ((d = magnify_generic(p, dwidth, dheight, method)) == NULL) return 0; free(p->image); p->image = d; p->left = p->left * dwidth / p->width; p->top = p->top * dheight / p->height; p->width = dwidth; p->height = dheight; p->bytes_per_line = image_calculate_bytes_per_line(dwidth, p->type); p->image_size = p->bytes_per_line * dheight; return 1; } int image_magnify(Image *p, double m, Interpolate_method method) { for (; p != NULL; p = p->next) if (!image_magnify_generic(p, p->width * m, p->height * m, method)) { fprintf(stderr, "image_magnify: No enough memory to magnify\n"); return 0; } return 1; } int image_flip_vertical(Image *p) { unsigned char *d; if ((d = flip_vertical(p)) == NULL) return 0; free(p->image); p->image = d; return 1; } int image_flip_horizontal(Image *p) { unsigned char *d; if ((d = flip_horizontal(p)) == NULL) return 0; free(p->image); p->image = d; return 1; } void image_copy_sub(Image *p, Image *q, int left, int top, int w, int h, int x, int y) { unsigned char *s = NULL, *d = NULL; int i, bytes = 0; /* image memory must be pre-allocated and same image type. */ if (q->image == NULL || p->type != q->type) return; switch (p->type) { case _MONO: /* aligned */ s = p->image + (int)(left / 8) + top * p->bytes_per_line; d = q->image + (int)(x / 8) + y * q->bytes_per_line; bytes = w / 8; if (left % 8) bytes++; if ((left + x) % 8) bytes++; break; case _GRAY: case _INDEX: s = p->image + left + top * p->bytes_per_line; d = q->image + x + y * q->bytes_per_line; bytes = w; break; case _RGB16: s = p->image + left * 2 + top * p->bytes_per_line; d = q->image + x * 2 + y * q->bytes_per_line; bytes = w * 2; break; case _RGB24: s = p->image + left * 3 + top * p->bytes_per_line; d = q->image + x * 3 + y * q->bytes_per_line; bytes = w * 3; break; case _UNKNOWN: default: fprintf(stderr, "image_copy_sub: Unknown image type.\n"); exit(-1); } /* actual copy routine */ for (i = 0; i < h; i++) { memcpy(d, s, bytes); s += p->bytes_per_line; d += q->bytes_per_line; } } void image_copy(Image *p, Image *q, int x, int y) { image_copy_sub(p, q, 0, 0, p->width, p->height, x, y); } Image * image_resize(Image *p, int width, int height, int gravity) { Image *q; if ((q = image_create(width, height, p->type)) == NULL) return NULL; memcpy(&q->ncolors, &p->ncolors, sizeof(int) * 6 + sizeof(unsigned char) * 256 * 3); if (p->width > width) if (p->height > height) switch (gravity) { case GRAVITY_MINUS: image_copy_sub(p, q, 0, 0, width, height, 0, 0); break; case GRAVITY_PLUS: image_copy_sub(p, q, p->width - width, p->height - height, width, height, 0, 0); break; case GRAVITY_CENTER: image_copy_sub(p, q, (p->width - width) / 2, (p->height - height) / 2, width, height, 0, 0); break; default: fprintf(stderr, "image_resize: Illegal gravity specification.\n"); exit(-1); } else switch (gravity) { case GRAVITY_MINUS: image_copy_sub(p, q, 0, 0, width, p->height, 0, 0); break; case GRAVITY_PLUS: image_copy_sub(p, q, p->width - width, 0, width, p->height, 0, height - p->height); break; case GRAVITY_CENTER: image_copy_sub(p, q, (p->width - width) / 2, 0, width, p->height, 0, (height - p->height) / 2); break; default: fprintf(stderr, "image_resize: Illegal gravity specification.\n"); exit(-1); } else if (p->height > height) switch (gravity) { case GRAVITY_MINUS: image_copy_sub(p, q, 0, 0, p->width, height, 0, 0); break; case GRAVITY_PLUS: image_copy_sub(p, q, 0, p->height - height, p->width, height, width - p->width, 0); break; case GRAVITY_CENTER: image_copy_sub(p, q, 0, (p->height - height) / 2, p->width, height, (width - p->width) / 2, 0); break; default: fprintf(stderr, "image_resize: Illegal gravity specification.\n"); exit(-1); } else switch (gravity) { case GRAVITY_MINUS: image_copy_sub(p, q, 0, 0, p->width, p->height, 0, 0); break; case GRAVITY_PLUS: image_copy_sub(p, q, 0, 0, p->width, p->height, width - p->width, height - p->height); break; case GRAVITY_CENTER: image_copy_sub(p, q, 0, 0, p->width, p->height, (width - p->width) / 2, (height - p->height) / 2); break; default: fprintf(stderr, "image_resize: Illegal gravity specification.\n"); exit(-1); } return q; } Image * image_connect(Image *p, Image *q, int flag) { int f, g, nwidth, nheight, nbpl; Image *r = NULL; if (p->type != q->type) return NULL; g = flag & CONNECT_VERTICAL; f = g ? (p->width != q->width) : (p->height != q->height); if (f) { if (!(flag & CONNECT_RESIZE)) return NULL; else if (g) if (p->width > q->width) { q = image_resize(q, p->width, q->height, GRAVITY_CENTER); f = 2; } else { p = image_resize(p, q->width, p->height, GRAVITY_CENTER); f = 1; } else if (p->height > p->height) { q = image_resize(q, q->width, p->height, GRAVITY_CENTER); f = 2; } else { p = image_resize(p, p->width, q->height, GRAVITY_CENTER); f = 1; } } if (g) { if (p->width != q->width) { fprintf(stderr, "image_connect: FATAL: No match width.\n"); exit(-1); } nwidth = p->width; nheight = p->height + q->height; nbpl = p->bytes_per_line; } else { if (p->height != q->height) { fprintf(stderr, "image_connect: FATAL: No match height.\n"); exit(-1); } nwidth = p->width + q->width; nheight = p->height; nbpl = p->bytes_per_line + q->bytes_per_line; } if ((r = image_create(nwidth, nheight, p->type)) == NULL) { if (f == 1) image_destroy(p); else if (f == 2) image_destroy(q); return NULL; } memcpy(&r->ncolors, &p->ncolors, sizeof(int) * 6 + sizeof(unsigned char) * 256 * 3); image_copy(p, r, 0, 0); if (g) image_copy(q, r, 0, p->height); else image_copy(q, r, p->width, 0); if (f == 1) image_destroy(p); else if (f == 2) image_destroy(q); return r; } #ifdef SHAPE static int image_create_mask_index(Image *p) { unsigned char *ptr = p->image, *d; unsigned char b; int tpi = p->transparent.index; int bit; int x, y; const int bit_mask[8] = {128, 64, 32, 16, 8, 4, 2, 1}; if (p->mask) return 1; p->mask_size = (int)((p->width + 7) / 8) * p->height; if ((p->mask = d = malloc(p->mask_size)) == NULL) return 0; for (y = 0; y < p->height; y++) { bit = 0; b = 0; for (x = 0; x < p->width; x++) { if (*ptr++ == tpi) b |= bit_mask[bit]; bit++; if (bit == 8) { *d++ = b; bit = 0; b = 0; } } if (bit > 0) *d++ = b; } return 1; } static int image_create_mask_rgb24(Image *p) { unsigned char *ptr = p->image, *d; unsigned char b; int tpr = p->transparent.red; int tpg = p->transparent.green; int tpb = p->transparent.blue; int bit, x, y; const int bit_mask[8] = {128, 64, 32, 16, 8, 4, 2, 1}; if (p->mask) return 1; p->mask_size = (int)((p->width + 7) / 8) * p->height; if ((p->mask = d = malloc(p->mask_size)) == NULL) return 0; for (y = 0; y < p->height; y++) { bit = 0; b = 0; for (x = 0; x < p->width; x++) { if (ptr[0] == tpr && ptr[1] == tpg && ptr[2] == tpb) b |= bit_mask[bit]; ptr += 3; bit++; if (bit == 8) { *d++ = b; bit = 0; b = 0; } } if (bit > 0) *d++ = b; } return 1; } int image_create_mask(Image *p) { switch (p->type) { case _INDEX: return image_create_mask_index(p); case _RGB24: return image_create_mask_rgb24(p); default: fprintf(stderr, "image_create_mask: Illegal image type: %d\n", p->type); exit(-1); } } #endif