Browse Source

* Big API reorganisation. Now libpipi can transparently convert between

colour spaces for a given image. For instance, if pipi_gaussian_blur is
    applied to a 32-bpp image, it is automatically converted to gamma-corrected
    32-bit floats beforehands, then converted back to normal.
  * TODO: clipping, regions of interest, more formats, getpixel macros...

git-svn-id: file:///srv/caca.zoy.org/var/lib/svn/libpipi/trunk@2605 92316355-f0b4-4df1-b90c-862c8a59935f
remotes/tiles
sam 16 years ago
parent
commit
31328a14aa
12 changed files with 293 additions and 153 deletions
  1. +0
    -17
      pipi/codec.c
  2. +29
    -14
      pipi/codec/imlib.c
  3. +29
    -14
      pipi/codec/opencv.c
  4. +31
    -16
      pipi/codec/sdl.c
  5. +3
    -0
      pipi/dither.c
  6. +19
    -11
      pipi/filter/blur.c
  7. +40
    -0
      pipi/pipi.c
  8. +30
    -13
      pipi/pipi.h
  9. +13
    -5
      pipi/pipi_internals.h
  10. +56
    -40
      pipi/pixels.c
  11. +28
    -19
      pipi/resize.c
  12. +15
    -4
      pipi/test.c

+ 0
- 17
pipi/codec.c View File

@@ -49,23 +49,6 @@ pipi_image_t *pipi_new(int width, int height)
#endif
}

pipi_image_t *pipi_copy(pipi_image_t const *img)
{
pipi_image_t *dst;
int x, y;
dst = pipi_new(img->width, img->height);
for(y = 0; y < img->height; y++)
{
for(x = 0; x < img->width; x++)
{
double r, g, b;
pipi_getpixel(img, x, y, &r, &g, &b);
pipi_setpixel(dst, x, y, r, g, b);
}
}
return dst;
}

void pipi_free(pipi_image_t *img)
{
#if USE_SDL


+ 29
- 14
pipi/codec/imlib.c View File

@@ -37,13 +37,20 @@ pipi_image_t *pipi_load_imlib2(const char *name)
return NULL;

img = (pipi_image_t *)malloc(sizeof(pipi_image_t));
memset(img, 0, sizeof(pipi_image_t));

imlib_context_set_image(priv);
img->width = imlib_image_get_width();
img->height = imlib_image_get_height();
img->pitch = 4 * imlib_image_get_width();
img->channels = 4;
img->pixels = (char *)imlib_image_get_data();
img->priv = (void *)priv;
img->w = imlib_image_get_width();
img->h = imlib_image_get_height();

img->p[PIPI_PIXELS_RGBA32].pixels = imlib_image_get_data();
img->p[PIPI_PIXELS_RGBA32].w = imlib_image_get_width();
img->p[PIPI_PIXELS_RGBA32].h = imlib_image_get_height();
img->p[PIPI_PIXELS_RGBA32].pitch = 4 * imlib_image_get_width();
img->last_modified = PIPI_PIXELS_RGBA32;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA32;

return img;
}
@@ -57,20 +64,27 @@ pipi_image_t *pipi_new_imlib2(int width, int height)
return NULL;

img = (pipi_image_t *)malloc(sizeof(pipi_image_t));
memset(img, 0, sizeof(pipi_image_t));

imlib_context_set_image(priv);
img->width = imlib_image_get_width();
img->height = imlib_image_get_height();
img->pitch = 4 * imlib_image_get_width();
img->channels = 4;
img->pixels = (char *)imlib_image_get_data();
img->priv = (void *)priv;
img->w = imlib_image_get_width();
img->h = imlib_image_get_height();

img->p[PIPI_PIXELS_RGBA32].pixels = imlib_image_get_data();
img->p[PIPI_PIXELS_RGBA32].w = imlib_image_get_width();
img->p[PIPI_PIXELS_RGBA32].h = imlib_image_get_height();
img->p[PIPI_PIXELS_RGBA32].pitch = 4 * imlib_image_get_width();
img->last_modified = PIPI_PIXELS_RGBA32;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA32;

return img;
}

void pipi_free_imlib2(pipi_image_t *img)
{
imlib_context_set_image(img->priv);
imlib_context_set_image(img->codec_priv);
imlib_free_image();

free(img);
@@ -78,7 +92,8 @@ void pipi_free_imlib2(pipi_image_t *img)

void pipi_save_imlib2(pipi_image_t *img, const char *name)
{
imlib_context_set_image(img->priv);
pipi_getpixels(img, img->codec_format);
imlib_context_set_image(img->codec_priv);
imlib_save_image(name);
}


+ 29
- 14
pipi/codec/opencv.c View File

@@ -38,12 +38,19 @@ pipi_image_t *pipi_load_opencv(const char *name)
return NULL;

img = (pipi_image_t *)malloc(sizeof(pipi_image_t));
img->width = priv->width;
img->height = priv->height;
img->pitch = priv->widthStep;
img->channels = priv->nChannels;
img->pixels = priv->imageData;
img->priv = (void *)priv;
memset(img, 0, sizeof(pipi_image_t));

img->w = priv->width;
img->h = priv->height;

img->p[PIPI_PIXELS_RGBA32].pixels = priv->imageData;
img->p[PIPI_PIXELS_RGBA32].w = priv->width;
img->p[PIPI_PIXELS_RGBA32].h = priv->height;
img->p[PIPI_PIXELS_RGBA32].pitch = priv->widthStep;
img->last_modified = PIPI_PIXELS_RGBA32;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA32;

return img;
}
@@ -57,12 +64,19 @@ pipi_image_t *pipi_new_opencv(int width, int height)
return NULL;

img = (pipi_image_t *)malloc(sizeof(pipi_image_t));
img->width = priv->width;
img->height = priv->height;
img->pitch = priv->widthStep;
img->channels = priv->nChannels;
img->pixels = priv->imageData;
img->priv = (void *)priv;
memset(img, 0, sizeof(pipi_image_t));

img->w = priv->width;
img->h = priv->height;

img->p[PIPI_PIXELS_RGBA32].pixels = priv->imageData;
img->p[PIPI_PIXELS_RGBA32].w = priv->width;
img->p[PIPI_PIXELS_RGBA32].h = priv->height;
img->p[PIPI_PIXELS_RGBA32].pitch = priv->widthStep;
img->last_modified = PIPI_PIXELS_RGBA32;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA32;

return img;
}
@@ -70,7 +84,7 @@ pipi_image_t *pipi_new_opencv(int width, int height)
void pipi_free_opencv(pipi_image_t *img)
{
IplImage *iplimg;
iplimg = (IplImage *)img->priv;
iplimg = (IplImage *)img->codec_priv;
cvReleaseImage(&iplimg);

free(img);
@@ -78,6 +92,7 @@ void pipi_free_opencv(pipi_image_t *img)

void pipi_save_opencv(pipi_image_t *img, const char *name)
{
cvSaveImage(name, img->priv);
pipi_getpixels(img, img->codec_format);
cvSaveImage(name, img->codec_priv);
}


+ 31
- 16
pipi/codec/sdl.c View File

@@ -36,21 +36,28 @@ pipi_image_t *pipi_load_sdl(const char *name)
if(!priv)
return NULL;

if(priv->format->BytesPerPixel == 1)
if(priv->format->BytesPerPixel != 4)
{
img = pipi_new(priv->w, priv->h);
SDL_BlitSurface(priv, NULL, img->priv, NULL);
SDL_BlitSurface(priv, NULL, img->codec_priv, NULL);
SDL_FreeSurface(priv);
return img;
}

img = (pipi_image_t *)malloc(sizeof(pipi_image_t));
img->width = priv->w;
img->height = priv->h;
img->pitch = priv->pitch;
img->channels = priv->format->BytesPerPixel;
img->pixels = priv->pixels;
img->priv = (void *)priv;
memset(img, 0, sizeof(pipi_image_t));

img->w = priv->w;
img->h = priv->h;

img->p[PIPI_PIXELS_RGBA32].pixels = priv->pixels;
img->p[PIPI_PIXELS_RGBA32].w = priv->w;
img->p[PIPI_PIXELS_RGBA32].h = priv->h;
img->p[PIPI_PIXELS_RGBA32].pitch = priv->pitch;
img->last_modified = PIPI_PIXELS_RGBA32;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA32;

return img;
}
@@ -78,25 +85,33 @@ pipi_image_t *pipi_new_sdl(int width, int height)
return NULL;

img = (pipi_image_t *)malloc(sizeof(pipi_image_t));
img->width = priv->w;
img->height = priv->h;
img->pitch = priv->pitch;
img->channels = priv->format->BytesPerPixel;
img->pixels = priv->pixels;
img->priv = (void *)priv;
memset(img, 0, sizeof(pipi_image_t));

img->w = priv->w;
img->h = priv->h;

img->p[PIPI_PIXELS_RGBA32].pixels = priv->pixels;
img->p[PIPI_PIXELS_RGBA32].w = priv->w;
img->p[PIPI_PIXELS_RGBA32].h = priv->h;
img->p[PIPI_PIXELS_RGBA32].pitch = priv->pitch;
img->last_modified = PIPI_PIXELS_RGBA32;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA32;

return img;
}

void pipi_free_sdl(pipi_image_t *img)
{
SDL_FreeSurface(img->priv);
SDL_FreeSurface(img->codec_priv);

free(img);
}

void pipi_save_sdl(pipi_image_t *img, const char *name)
{
SDL_SaveBMP(img->priv, name);
pipi_getpixels(img, img->codec_format);
SDL_SaveBMP(img->codec_priv, name);
}


+ 3
- 0
pipi/dither.c View File

@@ -28,6 +28,8 @@

void pipi_dither_24to16(pipi_image_t *img)
{
/* XXX: disabled because this is not the right place... see pixels.c instead */
#if 0
int *error, *nexterror;
uint32_t *p32;
int x, y;
@@ -84,5 +86,6 @@ void pipi_dither_24to16(pipi_image_t *img)

free(error);
free(nexterror);
#endif
}


+ 19
- 11
pipi/filter/blur.c View File

@@ -27,15 +27,24 @@
#include "pipi.h"
#include "pipi_internals.h"

pipi_image_t *pipi_gaussian_blur(pipi_image_t const *src, float radius)
pipi_image_t *pipi_gaussian_blur(pipi_image_t *src, float radius)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double *kernel;
double K, L, t = 0.;
int x, y, i, j, w, h, kr, kw;

w = src->width;
h = src->height;
w = src->w;
h = src->h;

srcp = pipi_getpixels(src, PIPI_PIXELS_RGBA_F);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = pipi_getpixels(dst, PIPI_PIXELS_RGBA_F);
dstdata = (float *)dstp->pixels;

kr = (int)(3. * radius + 0.99999);
kw = 2 * kr + 1;
@@ -52,8 +61,6 @@ pipi_image_t *pipi_gaussian_blur(pipi_image_t const *src, float radius)
for(i = -kr; i <= kr; i++)
kernel[(j + kr) * kw + (i + kr)] /= t;

dst = pipi_new(w, h);

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
@@ -63,15 +70,16 @@ pipi_image_t *pipi_gaussian_blur(pipi_image_t const *src, float radius)
for(j = -kr; j <= kr; j++)
for(i = -kr; i <= kr; i++)
{
double r, g, b, f = kernel[(j + kr) * kw + (i + kr)];
double f = kernel[(j + kr) * kw + (i + kr)];

pipi_getpixel(src, x + i, y + j, &r, &g, &b);
R += f * r;
G += f * g;
B += f * b;
R += f * srcdata[((y + j) * w + x + i) * 4];
G += f * srcdata[((y + j) * w + x + i) * 4 + 1];
B += f * srcdata[((y + j) * w + x + i) * 4 + 2];
}

pipi_setpixel(dst, x, y, R, G, B);
dstdata[(y * w + x) * 4] = R;
dstdata[(y * w + x) * 4 + 1] = G;
dstdata[(y * w + x) * 4 + 2] = B;
}
}



+ 40
- 0
pipi/pipi.c View File

@@ -0,0 +1,40 @@
/*
* libpipi Proper image processing implementation library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* All Rights Reserved
*
* $Id$
*
* This library is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/

/*
* pipi.c: core library routines
*/

#include "config.h"
#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pipi.h"
#include "pipi_internals.h"

/*
static int init = 0;

void _pipi_init(void)
{
if(init)
return;

_pipi_init_pixels();
}
*/


+ 30
- 13
pipi/pipi.h View File

@@ -24,25 +24,42 @@ extern "C"
{
#endif

/* pipi_format_t: this enum is a list of all possible pixel formats for
* our internal images. RGBA32 is the most usual format when an image has
* just been loaded, but RGBA_F is a lot better for complex operations. */
typedef enum
{
PIPI_PIXELS_RGBA32 = 0,
PIPI_PIXELS_RGBA_F = 1,

PIPI_PIXELS_MAX = 2
}
pipi_format_t;

/* pipi_pixels_t: this structure stores a pixel view of an image. */
typedef struct
{
void *pixels;
int w, h, pitch;
}
pipi_pixels_t;

/* pipi_image_t: the main image type */
typedef struct pipi_image pipi_image_t;

extern pipi_image_t *pipi_load(const char *name);
extern pipi_image_t *pipi_new(int width, int height);
extern pipi_image_t *pipi_copy(const pipi_image_t *img);
extern void pipi_free(pipi_image_t *img);
extern void pipi_save(pipi_image_t *img, const char *name);

extern int pipi_getgray(pipi_image_t const *img, int x, int y, int *g);
extern int pipi_getpixel(pipi_image_t const *img,
int x, int y, double *r, double *g, double *b);
extern int pipi_setpixel(pipi_image_t *img, int x, int y,
double r, double g, double b);
extern pipi_image_t *pipi_load(const char *);
extern pipi_image_t *pipi_new(int, int);
extern void pipi_free(pipi_image_t *);
extern void pipi_save(pipi_image_t *, const char *);

extern pipi_pixels_t *pipi_getpixels(pipi_image_t *, pipi_format_t);

extern pipi_image_t *pipi_resize(pipi_image_t const *, int, int);
extern pipi_image_t *pipi_resize(pipi_image_t *, int, int);

extern pipi_image_t *pipi_gaussian_blur(pipi_image_t const *, float);
extern pipi_image_t *pipi_gaussian_blur(pipi_image_t *, float);

extern void pipi_dither_24to16(pipi_image_t *img);
extern void pipi_dither_24to16(pipi_image_t *);

extern void pipi_test(pipi_image_t *);



+ 13
- 5
pipi/pipi_internals.h View File

@@ -16,14 +16,22 @@
* pipi_internals.h: internal types
*/

#ifndef __CACA_INTERNALS_H__
#define __CACA_INTERNALS_H__
#ifndef __PIPI_INTERNALS_H__
#define __PIPI_INTERNALS_H__

/* pipi_image_t: the image structure. This is probably going to be the most
* complex structure in the library, but right now it only has fairly normal
* stuff, like width and height and pointers to pixel areas. */
struct pipi_image
{
int width, height, pitch, channels;
unsigned char *pixels;
void *priv;
int w, h, pitch;
pipi_format_t codec_format, last_modified;

/* List of all possible pixel formats */
pipi_pixels_t p[PIPI_PIXELS_MAX];

/* Private data used by the codec */
void *codec_priv;
};

#ifdef USE_IMLIB2


+ 56
- 40
pipi/pixels.c View File

@@ -31,52 +31,68 @@
#define C2I(p) (pow(((double)p)/255., 2.2))
#define I2C(p) ((int)255.999*pow(((double)p), 1./2.2))

int pipi_getgray(pipi_image_t const *img, int x, int y, int *g)
/* Return a direct pointer to an image's pixels. */
pipi_pixels_t *pipi_getpixels(pipi_image_t *img, pipi_format_t type)
{
if(x < 0 || y < 0 || x >= img->width || y >= img->height)
{
*g = 255;
return -1;
}

*g = (unsigned char)img->pixels[y * img->pitch + x * img->channels + 1];
size_t bytes = 0;
int x, y, i;

return 0;
}

int pipi_getpixel(pipi_image_t const *img,
int x, int y, double *r, double *g, double *b)
{
uint8_t *pixel;
if(type < 0 || type >= PIPI_PIXELS_MAX)
return NULL;

if(x < 0) x = 0;
else if(x >= img->width) x = img->width - 1;

if(y < 0) y = 0;
else if(y >= img->height) y = img->height - 1;

pixel = img->pixels + y * img->pitch + x * img->channels;

*b = C2I((unsigned char)pixel[0]);
*g = C2I((unsigned char)pixel[1]);
*r = C2I((unsigned char)pixel[2]);

return 0;
}
if(img->last_modified == type)
return &img->p[type];

int pipi_setpixel(pipi_image_t *img, int x, int y, double r, double g, double b)
{
uint8_t *pixel;

if(x < 0 || y < 0 || x >= img->width || y >= img->height)
return -1;
/* Allocate pixels if necessary */
if(!img->p[type].pixels)
{
switch(type)
{
case PIPI_PIXELS_RGBA32:
bytes = img->w * img->h * 4 * sizeof(uint8_t);
break;
case PIPI_PIXELS_RGBA_F:
bytes = img->w * img->h * 4 * sizeof(float);
break;
default:
return NULL;
}

img->p[type].pixels = malloc(bytes);
}

pixel = img->pixels + y * img->pitch + x * img->channels;
/* Convert pixels */
if(img->last_modified == PIPI_PIXELS_RGBA32
&& type == PIPI_PIXELS_RGBA_F)
{
uint8_t *src = (uint8_t *)img->p[PIPI_PIXELS_RGBA32].pixels;
float *dest = (float *)img->p[type].pixels;

for(y = 0; y < img->h; y++)
for(x = 0; x < img->w; x++)
for(i = 0; i < 4; i++)
dest[4 * (y * img->w + x) + i]
= C2I(src[4 * (y * img->w + x) + i]);
}
else if(img->last_modified == PIPI_PIXELS_RGBA_F
&& type == PIPI_PIXELS_RGBA32)
{
float *src = (float *)img->p[PIPI_PIXELS_RGBA_F].pixels;
uint8_t *dest = (uint8_t *)img->p[type].pixels;

for(y = 0; y < img->h; y++)
for(x = 0; x < img->w; x++)
for(i = 0; i < 4; i++)
dest[4 * (y * img->w + x) + i]
= I2C(src[4 * (y * img->w + x) + i]);
}
else
{
memset(img->p[type].pixels, 0, bytes);
}

pixel[0] = I2C(b);
pixel[1] = I2C(g);
pixel[2] = I2C(r);
img->last_modified = type;

return 0;
return &img->p[type];
}


+ 28
- 19
pipi/resize.c View File

@@ -25,46 +25,54 @@
#include "pipi.h"
#include "pipi_internals.h"

pipi_image_t *pipi_resize(pipi_image_t const *src, int w, int h)
pipi_image_t *pipi_resize(pipi_image_t *src, int w, int h)
{
double *aline, *line;
float *srcdata, *dstdata, *aline, *line;
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
int x, y, x0, y0, sw, dw, sh, dh, remy;

srcp = pipi_getpixels(src, PIPI_PIXELS_RGBA_F);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = pipi_getpixels(dst, PIPI_PIXELS_RGBA_F);
dstdata = (float *)dstp->pixels;

sw = src->width; sh = src->height;
dw = dst->width; dh = dst->height;
sw = src->w; sh = src->h;
dw = dst->w; dh = dst->h;

aline = malloc(3 * dw * sizeof(double));
line = malloc(3 * dw * sizeof(double));
aline = malloc(3 * dw * sizeof(float));
line = malloc(3 * dw * sizeof(float));

memset(line, 0, 3 * dw * sizeof(double));
memset(line, 0, 3 * dw * sizeof(float));
remy = 0;

for(y = 0, y0 = 0; y < dst->height; y++)
for(y = 0, y0 = 0; y < dh; y++)
{
int toty = 0, ny;

memset(aline, 0, 3 * dw * sizeof(double));
memset(aline, 0, 3 * dw * sizeof(float));

while(toty < sh)
{
if(remy == 0)
{
double r = 0, g = 0, b = 0;
float r = 0, g = 0, b = 0;
int remx = 0;

for(x = 0, x0 = 0; x < dst->width; x++)
for(x = 0, x0 = 0; x < dw; x++)
{
double ar = 0, ag = 0, ab = 0;
float ar = 0, ag = 0, ab = 0;
int totx = 0, nx;

while(totx < sw)
{
if(remx == 0)
{
pipi_getpixel(src, x0, y0, &r, &g, &b);
r = srcdata[(y0 * sw + x0) * 4];
g = srcdata[(y0 * sw + x0) * 4 + 1];
b = srcdata[(y0 * sw + x0) * 4 + 2];
x0++;
remx = dw;
}
@@ -85,7 +93,7 @@ pipi_image_t *pipi_resize(pipi_image_t const *src, int w, int h)
}

ny = (toty + remy <= sh) ? remy : sh - toty;
for(x = 0; x < dst->width; x++)
for(x = 0; x < dw; x++)
{
aline[3 * x] += ny * line[3 * x];
aline[3 * x + 1] += ny * line[3 * x + 1];
@@ -95,11 +103,12 @@ pipi_image_t *pipi_resize(pipi_image_t const *src, int w, int h)
remy -= ny;
}

for(x = 0; x < dst->width; x++)
pipi_setpixel(dst, x, y,
aline[3 * x] / (sw * sh),
aline[3 * x + 1] / (sw * sh),
aline[3 * x + 2] / (sw * sh));
for(x = 0; x < dw; x++)
{
dstdata[(y * dw + x) * 4] = aline[3 * x] / (sw * sh);
dstdata[(y * dw + x) * 4 + 1] = aline[3 * x + 1] / (sw * sh);
dstdata[(y * dw + x) * 4 + 2] = aline[3 * x + 2] / (sw * sh);
}
}

free(aline);


+ 15
- 4
pipi/test.c View File

@@ -27,15 +27,23 @@

void pipi_test(pipi_image_t *img)
{
pipi_pixels_t *pixels;
float *data;
int x, y;

for(y = 0; y < img->height; y++)
pixels = pipi_getpixels(img, PIPI_PIXELS_RGBA_F);
data = (float *)pixels->pixels;

for(y = 0; y < img->h; y++)
{
for(x = 0; x < img->width; x++)
for(x = 0; x < img->w; x++)
{
double r = 0, g = 0, b = 0;

pipi_getpixel(img, x, y, &r, &g, &b);
r = data[(y * img->w + x) * 4];
g = data[(y * img->w + x) * 4 + 1];
b = data[(y * img->w + x) * 4 + 2];

if(r + g + b == 0)
r = g = b = 1. / 3;
else if(r + g + b < 1.)
@@ -53,7 +61,10 @@ void pipi_test(pipi_image_t *img)
double d = (-1. - r + g + b) / 3;
r += d; g -= d; b -= d;
}
pipi_setpixel(img, x, y, r, g, b);

data[(y * img->w + x) * 4] = r;
data[(y * img->w + x) * 4 + 1] = g;
data[(y * img->w + x) * 4 + 2] = b;
}
}
}


Loading…
Cancel
Save