libtthimage/Lib/image.c

650 lines
17 KiB
C
Raw Normal View History

2022-06-26 02:23:53 +02:00
/*
2022-06-26 22:55:56 +02:00
fonctions de bases pour la librairie 'libtthimage'
--------------------------------------------------
2022-06-26 02:23:53 +02:00
(g) 1992,2003 - Thierry Boudet - aka Oulala
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2022-06-26 22:55:56 +02:00
#include "../tthimage.h"
2022-06-26 02:23:53 +02:00
/*::------------------------------------------------------------------::*/
2023-09-23 20:00:14 +02:00
void Image_print_version(int flag)
2022-06-26 02:23:53 +02:00
{
char *ptr;
fflush(stdout);
2024-07-17 02:39:21 +02:00
fprintf(stderr, "-+- This is the `tthimage' library v%s (wtfyw 2024) tTh\n",
2022-06-26 02:23:53 +02:00
IMAGE_VERSION_STRING);
if (flag)
{
/* this information is only correct IF you touch image.c before
* running 'make' utility */
2023-09-23 20:00:14 +02:00
fprintf(stderr, " + compiled : %s, %s\n", __DATE__, __TIME__);
2022-06-26 02:23:53 +02:00
}
if (flag > 1)
{
2023-09-23 20:00:14 +02:00
fprintf(stderr, " + DESTDIR = %s\n", DESTDIR);
fprintf(stderr, " + SHAREDIR = %s\n", SHAREDIR);
fprintf(stderr, " + CC OPTS = %s\n", CC_OPTS);
2022-06-26 02:23:53 +02:00
}
fflush(stdout);
/* OMFG, this is a gnuism ! */
if ( (ptr=getenv("MALLOC_CHECK_")) != NULL)
{
2022-06-26 22:55:56 +02:00
fprintf(stderr, "-+- libtthimage: malloc check is set to %s\n", ptr);
2022-06-26 02:23:53 +02:00
}
#if DEBUG_LEVEL
fprintf(stderr,
2022-06-26 22:55:56 +02:00
"Warning! this version of libtthimage is compiled with DEBUG_LEVEL=%d\n",
2022-06-26 02:23:53 +02:00
DEBUG_LEVEL);
#if FORCE_ABORT
2022-06-26 22:55:56 +02:00
fprintf(stderr, "Warning! this version of libtthimage is compiled with FORCE_ABORT!\n");
2022-06-26 02:23:53 +02:00
#endif
#endif
}
/*::------------------------------------------------------------------::*/
/*
* This function create a new channel for an image.
* Before version 0.4.9 it was a private function, but
* the module 'alpha.c' need access to it.
*
* ... So I make it "semi-public" ... */
int Image_alloc_pixels(unsigned char ***pix, int w, int h);
/*
* pour le moment, pas de code retour en cas
* d'erreur, mais un exit(1) brutal.
*/
int Image_alloc_pixels(unsigned char ***pix, int w, int h)
{
int foo;
unsigned char **lignes, *ptr;
lignes = (unsigned char **)malloc(sizeof(unsigned char *) * h);
#if DEBUG_LEVEL > 1
fprintf(stderr, " alloc pixels for plane @ %p -> %p\n", pix, lignes);
#endif
*pix = lignes; /* setting the indirected return value here
so we can use 'lignes' in the loop. */
for (foo=0; foo<h; foo++)
{
if ((ptr=(unsigned char *)malloc(sizeof(unsigned char) * w))==NULL)
{
fprintf(stderr, "%s: malloc failed for image line %d\n", __func__, foo);
exit(1);
}
*lignes++ = ptr;
}
return 0;
}
/*::------------------------------------------------------------------::*/
/*
* This function allocate an image and return a pointer
* on the structure who describe the image.
* - - - > don't "free" this pointer, please.
*/
Image_Desc *
Image_alloc(int width, int height, int type)
{
Image_Desc *header;
int foo;
#if DEBUG_LEVEL > 1
fprintf(stderr, "allocation for an image %dx%d, type:%d\n",
width, height, type);
#endif
if ( (header=(Image_Desc *)calloc(sizeof(Image_Desc), 1)) == NULL )
{
fprintf(stderr, "Image_Alloc (%s) err malloc header\n", __FILE__);
exit(1);
}
#if DEBUG_LEVEL > 1
fprintf(stderr, " header is at @ %p\n", header);
#endif
strcpy(header->name, "");
header->nb_planes = type;
header->width = width; header->height = height;
#if IMGCOMMENT
strcpy(header->comment, "* libimg is (dwtfywl) tontonTh 2022 *");
#else
/*
* The old 'Xv' image viewer does not like .TGA with a comment.
*/
strcpy(header->comment, "");
#endif
header->type = type;
header->Rpix = header->Gpix = header->Bpix = header->Apix = NULL;
header->magic = MAGIC_OF_IMAGE;
header->nbcols = 0;
header->idx_palette = 0;
header->errmsg = header->modified = 0;
/* nouveau 18 Sept 2001 */
header->fx = header->fy = 0.0;
header->fw = (double)width;
header->fh = (double)height;
/* new 12 Nov 2001 - see 'turtle.c' */
header->rt = header->gt = header->bt = 42;
header->xt = (double)width / 2.0;
header->yt = (double)height / 2.0;
header->at = 0; /* canal alpha, 0 est-il la bonne valeur ? */
switch (type)
{
case IMAGE_GREY:
Image_alloc_pixels(&header->Rpix, width, height);
for (foo=0; foo<256; foo++)
{
header->palette[0][foo] = foo;
header->palette[1][foo] = foo;
header->palette[2][foo] = foo;
}
break;
case IMAGE_RGB:
Image_alloc_pixels(&header->Rpix, width, height);
Image_alloc_pixels(&header->Gpix, width, height);
Image_alloc_pixels(&header->Bpix, width, height);
break;
case IMAGE_RGBA:
#if DEBUG_LEVEL
fprintf(stderr, "WARNING! allocating an image with alpha channel,\n");
#endif
Image_alloc_pixels(&header->Rpix, width, height);
Image_alloc_pixels(&header->Gpix, width, height);
Image_alloc_pixels(&header->Bpix, width, height);
Image_alloc_pixels(&header->Apix, width, height);
break;
default:
fprintf(stderr, "***** %s alloc image: %d is an unknow type *****\n",
__FILE__, type);
return NULL;
}
if (getenv("MALLOC_CHECK_") != NULL)
{
2022-06-26 22:55:56 +02:00
fprintf(stderr, "-+- libtthimage %s %d: alloc %p\n",
2022-06-26 02:23:53 +02:00
__FILE__, __LINE__, header);
}
return header;
}
/*::------------------------------------------------------------------::*/
/*
This fonction build another image from a model.
*/
Image_Desc *
Image_clone (Image_Desc *src, int copy)
{
Image_Desc *image;
if ( src==NULL )
{
fprintf(stderr, "Image_clone: source descriptor is NULL\n");
exit(5);
}
image = Image_alloc(src->width, src->height, src->type);
if ( image==NULL )
{
fprintf(stderr, "Image_clone: cloned descriptor is NULL\n");
exit(5);
}
/*
* we have to transfer a few attributes ...
*/
#if DEBUG_LEVEL > 1
fprintf(stderr, "clone %p to %p, no copy of attributes ?\n", src, image);
#endif
image->fx = src->fx; image->fy = src->fy;
image->fw = src->fw; image->fh = src->fh;
if (copy == 1)
{
Image_copy(src, image);
}
image->modified = 0; /* 8 Fev 01 */
return image;
}
/*::------------------------------------------------------------------::*/
int Image_copy_comment(Image_Desc *s, Image_Desc *d)
{
#if DEBUG_LEVEL
fprintf(stderr, "%s ( %p -> %p )\n", __func__, s, d);
#endif
/* TODO check the validity of the comment, there _must_ be a '\0' inside */
memcpy(d->comment, s->comment, IMG_OBJCOMMENT_LEN);
return FUNC_IS_ALPHA;
}
/*::------------------------------------------------------------------::*/
/*
* Helas, cette fonction ne marche que sur les images RGB
* et comment la rendre compatible tout-types sans tout casser ?
*/
int
Image_clear( Image_Desc *image, int r, int g, int b )
{
int x, y;
if (image->type == IMAGE_RGB)
{
for (y=0; y<image->height; y++)
{
/*
* XXX here we can go faster with a few memset
*/
for (x=0; x<image->width; x++)
{
(image->Rpix[y])[x] = r;
(image->Gpix[y])[x] = g;
(image->Bpix[y])[x] = b;
/* Bon, et le canal Alpha, il devient quoi ? */
}
}
return 0; /* ok, this 'return' here is a "spleyterie" :) */
}
if (image->type == IMAGE_RGBA)
{
for (y=0; y<image->height; y++)
{
for (x=0; x<image->width; x++)
{
(image->Rpix[y])[x] = r;
(image->Gpix[y])[x] = g;
(image->Bpix[y])[x] = b;
(image->Apix[y])[x] = 0;
}
}
return 0; /* ok, this 'return' here is a "spleyterie" :) */
}
fprintf(stderr, "%s : invalid image type %d : %s\n", __func__,
image->type, Image_type2str(image->type));
return IMAGE_BAD_TYPE;
}
/*::------------------------------------------------------------------::*/
2023-09-25 13:28:13 +02:00
/* new: Sat Sep 23 19:07:03 UTC 2023
*
*/
int Image_set_rgb(Image_Desc *img, RGBA *rgba)
{
int x, y;
#if DEBUG_LEVEL
fprintf(stderr, ">>> %s ( %p %p ) %d %d %d\n", __func__, img,
rgba, rgba->r, rgba->g, rgba->b);
#endif
if (IMAGE_RGB != img->type) {
fprintf(stderr, "%s : invalid image type %d : %s\n", __func__,
img->type, Image_type2str(img->type));
return IMAGE_BAD_TYPE;
}
for (y=0; y<img->height; y++) {
for (x=0; x<img->width; x++) {
(img->Rpix[y])[x] = rgba->r;
(img->Gpix[y])[x] = rgba->g;
(img->Bpix[y])[x] = rgba->b;
}
}
return FUNC_IS_BETA;
}
/*::------------------------------------------------------------------::*/
2022-06-26 02:23:53 +02:00
/*
* every image in memory have a comment field, who is writen
* in TGA and PNM file when image is saved.
*/
int
Image_set_comment(Image_Desc *image, char *text)
{
if (strlen(text) > IMG_OBJCOMMENT_LEN)
return STRING_TOO_LONG;
strcpy(image->comment, text);
return OLL_KORRECT;
}
/* 10 nov 2001: no #define for this not-so-magic 254 value ? */
/*::------------------------------------------------------------------::*/
int
Image_plot_gray(Image_Desc *img, int x, int y, int v)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
fprintf(stderr, "ERRPLOT X %4d Y %4d %d\n", x, y, v);
return OUT_OF_IMAGE;
}
(img->Rpix[y])[x] = (uint8_t)v;
/* POURQUOI on ne plotte qu'une composante ? XXX */
return OLL_KORRECT;
}
int Image_plotRGB(Image_Desc *img, int x, int y, int r, int g, int b)
{
#if DEBUG_LEVEL > 2
fprintf(stderr, "PLOTRGB %d %d\n", x, y);
#endif
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
fprintf(stderr, "Errplot RGB X=%d, Y=%d %d, %d, %d\n", x, y, r, g, b);
#if FORCE_ABORT
abort();
#endif
return OUT_OF_IMAGE;
}
(img->Rpix[y])[x] = (uint8_t)(r&0xff);
(img->Gpix[y])[x] = (uint8_t)(g&0xff);
(img->Bpix[y])[x] = (uint8_t)(b&0xff);
return OLL_KORRECT;
}
int Image_plotRGBA(Image_Desc *img, int x, int y, int r, int g, int b, int a)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
/* may be an #if DEBUG_LEVEL here ? */
fprintf(stderr, "Errplot RGBA X %4d Y %4d %d, %d, %d, %d\n",
x, y, r, g, b, a);
#if FORCE_ABORT
abort();
#endif
return OUT_OF_IMAGE;
}
(img->Rpix[y])[x] = (uint8_t)(r&0xff);
(img->Gpix[y])[x] = (uint8_t)(g&0xff);
(img->Bpix[y])[x] = (uint8_t)(b&0xff);
(img->Apix[y])[x] = (uint8_t)(a&0xff);
return OLL_KORRECT;
}
/*::------------------------------------------------------------------::*/
int
Image_getRGB(Image_Desc *img, int x, int y, int *pr, int *pg, int *pb)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
/* may be an #if DEBUG_LEVEL here ? */
fprintf(stderr, "ERR GETRGB X %4d Y %4d\n", x, y);
#if FORCE_ABORT
abort();
#endif
return OUT_OF_IMAGE;
}
*pr = (int)((img->Rpix[y])[x]);
*pg = (int)((img->Gpix[y])[x]);
*pb = (int)((img->Bpix[y])[x]);
return OLL_KORRECT;
}
/*::------------------------------------------------------------------::*/
int
Image_getRGBA(Image_Desc *img, int x, int y, int *pr, int *pg, int *pb, int *pa)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
fprintf(stderr, "ERR GETRGBA X %4d Y %4d\n", x, y);
#if FORCE_ABORT
abort();
#endif
return OUT_OF_IMAGE;
}
if (img->type != IMAGE_RGBA)
{
fprintf(stderr, "Image get RGBA: bad image type: %d, %s\n",
img->type, Image_type2str(img->type));
#if FORCE_ABORT
abort();
#endif
exit(7); /* et un code d'erreur, <20>a irais pas mieux */
}
*pr = (int)((img->Rpix[y])[x]);
*pg = (int)((img->Gpix[y])[x]);
*pb = (int)((img->Bpix[y])[x]);
*pa = (int)((img->Apix[y])[x]);
return OLL_KORRECT;
}
/*::------------------------------------------------------------------::*/
/*
* ecriture d'une des composantes de l'image. (new=2000-02-01)
*
* 15 mars 2003: l'interface Fortran est dans 'img77g.c' XXX
*/
int Image_plot_channel(Image_Desc *img, char channel, int x, int y, int value)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
#if DEBUG_LEVEL
fprintf(stderr, "ERR PLOTCHANNEL X %4d Y %4d\n", x, y);
#endif
return OUT_OF_IMAGE;
}
switch (channel)
{
case 'r': case 'R': (img->Rpix[y])[x] = (uint8_t)(value&0xff); break;
case 'g': case 'G': (img->Gpix[y])[x] = (uint8_t)(value&0xff); break;
case 'b': case 'B': (img->Bpix[y])[x] = (uint8_t)(value&0xff); break;
case 'a': case 'A': (img->Apix[y])[x] = (uint8_t)(value&0xff); break;
default: return WRONG_CHANNEL;
}
return OLL_KORRECT;
}
/*::------------------------------------------------------------------::*/
/*
lecture d'une des composantes de l'image.
*/
int
Image_R_pixel(Image_Desc *img, int x, int y)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
fprintf(stderr, "ERR READ R PIX X%d Y%d\n", x, y);
return OUT_OF_IMAGE;
}
return (int)((img->Rpix[y])[x]);
}
int
Image_G_pixel(Image_Desc *img, int x, int y)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
fprintf(stderr, "ERR READ G PIX X%d Y%d\n", x, y);
return OUT_OF_IMAGE;
}
return (int)((img->Gpix[y])[x]);
}
int
Image_B_pixel(Image_Desc *img, int x, int y)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
fprintf(stderr, "ERR READ B PIX X%d Y%d\n", x, y);
return OUT_OF_IMAGE;
}
return (int)((img->Bpix[y])[x]);
}
int
Image_A_pixel(Image_Desc *img, int x, int y)
{
if ( x<0 || y<0 || x>=img->width || y>=img->height )
{
fprintf(stderr, "ERR A PIX X%d Y%d\n", x, y);
return OUT_OF_IMAGE;
}
return (int)((img->Apix[y])[x]);
}
/*::------------------------------------------------------------------::*/
2022-06-26 11:06:35 +02:00
/*
cette fonction ne marche que si les deux images
sont allouees et de meme dimensions.
*/
2022-09-17 11:23:21 +02:00
int Image_copy(Image_Desc *src, Image_Desc *dst)
2022-06-26 11:06:35 +02:00
{
int foo;
2022-09-17 11:23:21 +02:00
if ( (foo=Image_compare_desc(src, dst)) ) {
fprintf(stderr, "%s: images are differents %d\n", __func__, foo);
2022-06-26 11:06:35 +02:00
return foo;
}
2022-09-17 11:23:21 +02:00
for (foo=0; foo<src->height; foo++) {
2022-06-26 11:06:35 +02:00
memcpy(dst->Rpix[foo], src->Rpix[foo], src->width);
memcpy(dst->Gpix[foo], src->Gpix[foo], src->width);
memcpy(dst->Bpix[foo], src->Bpix[foo], src->width);
if (dst->Apix!=NULL && src->Apix!=NULL)
memcpy(dst->Apix[foo], src->Apix[foo], src->width);
}
dst->modified = 1;
/*
>> 18 Juin 2002
>> ============
>> bon, maintenant se pose un grand probl<EFBFBD>me philosophique.
>> on vient juste de copier les octets qui incarnent les pixels
>> rgb et peut-<EFBFBD>tre alpha. Mais ce n'est que la partie <EFBFBD>vident
>> d'une image: ses pixels.
*/
return 0;
}
/*::------------------------------------------------------------------::*/
2022-06-26 02:23:53 +02:00
/*
* no boundary control ?
*/
2022-09-17 11:23:21 +02:00
int Image_pixel_copy(Image_Desc *s, int x, int y, Image_Desc *d, int i, int j)
2022-06-26 02:23:53 +02:00
{
(d->Rpix[j])[i] = (s->Rpix[y])[x];
(d->Gpix[j])[i] = (s->Gpix[y])[x];
(d->Bpix[j])[i] = (s->Bpix[y])[x];
return OLL_KORRECT;
}
/*::------------------------------------------------------------------::*/
/*
* Le nom de cette fonction n'a pas trop de rapport avec ce qu'elle fait,
* mais elle permet de faire reculer Crash Coredump.
*/
2022-09-17 11:23:21 +02:00
int Image_compare_desc(Image_Desc *a, Image_Desc *b)
2022-06-26 02:23:53 +02:00
{
char *fmt = "Image at %p have no 'Dead Beef' in it\n";
if ( (a==NULL) || (b==NULL) ) return NULL_DESCRIPTOR;
if ( a->magic != MAGIC_OF_IMAGE )
{
fprintf(stderr, fmt, a);
return NOT_AN_IMAGE_DESC;
}
if ( b->magic != MAGIC_OF_IMAGE )
{
fprintf(stderr, fmt, a);
return NOT_AN_IMAGE_DESC;
}
if ( a->width != b->width ) return DIFFERENT_WIDTH;
if ( a->height!= b->height) return DIFFERENT_HEIGHT;
return OLL_KORRECT;
}
/*::------------------------------------------------------------------::*/
/*
Cette fonction ne rend pas toute la memoire
Pour le moment, le header n'est pas restitue.
le probleme, si on desalloue le header
c'est qu'il reste un pointeur fou chez
l'appelant ...
*/
int
Image_DeAllocate(Image_Desc *im)
{
int line;
if (getenv("MALLOC_CHECK_") != NULL) {
2022-06-26 22:55:56 +02:00
fprintf(stderr, "-+- libtthimage %s %d: free %p\n",
2022-06-26 02:23:53 +02:00
__FILE__, __LINE__, im);
}
if ( im->magic != MAGIC_OF_IMAGE ) {
fprintf(stderr, "at %p, there was no 'Dead Beef' to deallocate\n", im);
/* emergency exit */
abort();
}
#if DEBUG_LEVEL > 1
fprintf(stderr, ">> de-allocating image %p, cross your fingers.\n", im);
fprintf(stderr, " %p %p %p %p\n", im->Rpix, im->Gpix, im->Bpix, im->Apix);
#endif
im->magic = 0L; /* mark dirty/invalid. */
im->type = IMAGE_NONE; /* -1, et tusors */
2022-09-13 00:11:53 +02:00
if (im->Rpix != NULL) {
2022-06-26 02:23:53 +02:00
for (line=0; line<im->height; line++)
if (im->Rpix[line] != NULL) free(im->Rpix[line]);
free(im->Rpix); im->Rpix = NULL;
}
2022-09-13 00:11:53 +02:00
if (im->Gpix != NULL) {
2022-06-26 02:23:53 +02:00
for (line=0; line<im->height; line++)
if (im->Gpix[line] != NULL) free(im->Gpix[line]);
free(im->Gpix); im->Gpix = NULL;
}
2022-09-13 00:11:53 +02:00
if (im->Bpix != NULL) {
2022-06-26 02:23:53 +02:00
for (line=0; line<im->height; line++)
if (im->Bpix[line] != NULL) free(im->Bpix[line]);
free(im->Bpix); im->Bpix = NULL;
}
2022-09-13 00:11:53 +02:00
if (im->Apix != NULL) {
2022-06-26 02:23:53 +02:00
for (line=0; line<im->height; line++)
if (im->Apix[line] != NULL) free(im->Apix[line]);
free(im->Apix); im->Apix = NULL;
}
#if DEBUG_LEVEL > 1
fprintf(stderr, "DeAllocate >> are we alive ?\n");
#endif
return OLL_KORRECT;
}
/*::------------------------------------------------------------------::*/