Parcourir la source

image: import old libpipi code, hoping it can be upgraded to C++ and

merged iteratively with our current image processing code.
undefined
Sam Hocevar il y a 10 ans
Parent
révision
ce1d39cc3a
58 fichiers modifiés avec 10234 ajouts et 0 suppressions
  1. +65
    -0
      src/image/accessors.cpp
  2. +178
    -0
      src/image/analysis/histogram.cpp
  3. +101
    -0
      src/image/analysis/measure.cpp
  4. +124
    -0
      src/image/codec.cpp
  5. +180
    -0
      src/image/codec/coreimage.cpp
  6. +38
    -0
      src/image/codec/coreimage.h
  7. +148
    -0
      src/image/codec/gdi.cpp
  8. +120
    -0
      src/image/codec/imlib.cpp
  9. +249
    -0
      src/image/codec/jpeg.cpp
  10. +112
    -0
      src/image/codec/opencv.cpp
  11. +588
    -0
      src/image/codec/oric.cpp
  12. +133
    -0
      src/image/codec/sdl.cpp
  13. +127
    -0
      src/image/colorstring.cpp
  14. +89
    -0
      src/image/combine/blit.cpp
  15. +86
    -0
      src/image/combine/merge.cpp
  16. +123
    -0
      src/image/combine/minmax.cpp
  17. +221
    -0
      src/image/combine/mulscreen.cpp
  18. +135
    -0
      src/image/combine/rgb.cpp
  19. +173
    -0
      src/image/combine/subadd.cpp
  20. +736
    -0
      src/image/context.cpp
  21. +68
    -0
      src/image/crop.cpp
  22. +93
    -0
      src/image/dither.cpp
  23. +228
    -0
      src/image/dither/dbs.cpp
  24. +86
    -0
      src/image/dither/ediff.cpp
  25. +144
    -0
      src/image/dither/ordered.cpp
  26. +117
    -0
      src/image/dither/ostromoukhov.cpp
  27. +60
    -0
      src/image/dither/random.cpp
  28. +111
    -0
      src/image/filter/autocontrast.cpp
  29. +315
    -0
      src/image/filter/blur.cpp
  30. +288
    -0
      src/image/filter/color.cpp
  31. +289
    -0
      src/image/filter/convolution.cpp
  32. +170
    -0
      src/image/filter/dilate.cpp
  33. +134
    -0
      src/image/filter/median.cpp
  34. +100
    -0
      src/image/filter/rotate.cpp
  35. +128
    -0
      src/image/filter/sharpen.cpp
  36. +252
    -0
      src/image/filter/transform.cpp
  37. +177
    -0
      src/image/filter/wave.cpp
  38. +127
    -0
      src/image/filter/yuv.cpp
  39. +58
    -0
      src/image/paint/bezier.cpp
  40. +318
    -0
      src/image/paint/floodfill.cpp
  41. +470
    -0
      src/image/paint/line.cpp
  42. +47
    -0
      src/image/paint/rectangle.cpp
  43. +56
    -0
      src/image/paint/tile.cpp
  44. +138
    -0
      src/image/pipi-internals.h
  45. +97
    -0
      src/image/pipi-stubs.h
  46. +80
    -0
      src/image/pipi-template.h
  47. +100
    -0
      src/image/pipi-types.h
  48. +96
    -0
      src/image/pipi.cpp
  49. +253
    -0
      src/image/pipi.h
  50. +285
    -0
      src/image/pixels.cpp
  51. +509
    -0
      src/image/quantize/reduce.cpp
  52. +61
    -0
      src/image/render/noise.cpp
  53. +128
    -0
      src/image/render/screen.cpp
  54. +136
    -0
      src/image/resample/bicubic.cpp
  55. +131
    -0
      src/image/resample/bresenham.cpp
  56. +346
    -0
      src/image/sequence.cpp
  57. +201
    -0
      src/image/stock.cpp
  58. +111
    -0
      src/image/tiles.cpp

+ 65
- 0
src/image/accessors.cpp Voir le fichier

@@ -0,0 +1,65 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* accessors.c: accessors for various informations about images
*/

#include "config.h"

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

#include <math.h>

#include "pipi.h"
#include "pipi-internals.h"

int pipi_get_image_width(pipi_image_t *img)
{
return img->w;
}
int pipi_get_image_height(pipi_image_t *img)
{
return img->h;
}
int pipi_get_image_pitch(pipi_image_t *img)
{
return img->pitch;
}
int pipi_get_image_last_modified(pipi_image_t *img)
{
return img->last_modified;
}



char formats[][100] =
{
"Unknow",
"RGBA_C",
"BGR_C",
"RGBA_F",
"Y_F",
"MASK_C",
"LOL",
};

const char* pipi_get_format_name(int format)
{
if(format>PIPI_PIXELS_MAX) return "Invalid";
else return formats[format];
}

+ 178
- 0
src/image/analysis/histogram.cpp Voir le fichier

@@ -0,0 +1,178 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* histogram.c: histogram functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"


pipi_histogram_t* pipi_new_histogram(void)
{
return malloc(sizeof(pipi_histogram_t));
}


int pipi_get_image_histogram(pipi_image_t *img, pipi_histogram_t*h, int flags)
{
pipi_pixels_t *p;
uint8_t *data;
float n;
unsigned int max;
int i;

if(!h) return -1;

p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);
data = (uint8_t *)p->pixels;
memset(h->a, 0, 256*(sizeof(unsigned int)));
memset(h->r, 0, 256*(sizeof(unsigned int)));
memset(h->g, 0, 256*(sizeof(unsigned int)));
memset(h->b, 0, 256*(sizeof(unsigned int)));
memset(h->y, 0, 256*(sizeof(unsigned int)));


for(i=0; i< img->w*img->h*4; i+=4)
{
if(flags&PIPI_COLOR_A)
h->a[data[i+3]]++;
if(flags&PIPI_COLOR_R)
h->r[data[i+2]]++;
if(flags&PIPI_COLOR_G)
h->g[data[i+1]]++;
if(flags&PIPI_COLOR_B)
h->b[data[i]]++;
if(flags&PIPI_COLOR_Y)
{
uint32_t val = 0.;
val += 0.299 * data[i];
val += 0.587 * data[i+1];
val += 0.114 * data[i+2];

h->y[val>255?255:val]++;
}
}

/* Normalize dataset */
if(flags&PIPI_COLOR_R)
{
max = 0;
for(i=0; i<256; i++)
if(h->r[i] > max) max = h->r[i];
n = 255.0f / max;
for(i=0; i<256; i++)
h->r[i]*=n;
}
if(flags&PIPI_COLOR_G)
{
max = 0;
for(i=0; i<256; i++)
if(h->g[i] > max) max = h->g[i];
n = 255.0f / max;
for(i=0; i<256; i++)
h->g[i]*=n;
}
if(flags&PIPI_COLOR_B)
{
max = 0;
for(i=0; i<256; i++)
if(h->b[i] > max) max = h->b[i];
n = 255.0f / max;
for(i=0; i<256; i++)
h->b[i]*=n;
}
if(flags&PIPI_COLOR_A)
{
max = 0;
for(i=0; i<256; i++)
if(h->a[i] > max) max = h->a[i];
n = 255.0f / max;
for(i=0; i<256; i++)
h->a[i]*=n;
}
if(flags&PIPI_COLOR_Y)
{
max = 0;
for(i=0; i<256; i++)
if(h->y[i] > max) max = h->y[i];
n = 255.0f / max;
for(i=0; i<256; i++)
h->y[i]*=n;
}

pipi_release_pixels(img, p);

return 0;
}

int pipi_render_histogram(pipi_image_t *img, pipi_histogram_t*h, int flags)
{
int x;

if(!img || !h) return -1;

for(x=0; x<256; x++)
{
if(flags&PIPI_COLOR_R)
pipi_draw_line(img,
x, 255,
x, 255 - h->r[x],
0x00FF0000,
0);
if(flags&PIPI_COLOR_G)
pipi_draw_line(img,
x, 255,
x, 255 - h->g[x],
0x0000FF00,
0);
if(flags&PIPI_COLOR_B)
pipi_draw_line(img,
x, 255,
x, 255 - h->b[x],
0x000000FF,
0);
if(flags&PIPI_COLOR_A)
pipi_draw_line(img,
x, 255,
x, 255 - h->a[x],
0x00000FFF,
0);
if(flags&PIPI_COLOR_Y)
pipi_draw_line(img,
x, 255,
x, 255 - h->y[x],
0x00FFFFFF,
0);
}

return 0;
}


int pipi_free_histogram(pipi_histogram_t* h)
{
if(h) free(h);
else return -1;

return 0;
}

+ 101
- 0
src/image/analysis/measure.cpp Voir le fichier

@@ -0,0 +1,101 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* measure.c: distance functions
*/

#include "config.h"

#include <math.h>

#include "pipi.h"
#include "pipi-internals.h"

double pipi_measure_rmsd(pipi_image_t *i1, pipi_image_t *i2)
{
return sqrt(pipi_measure_msd(i1, i2));
}

double pipi_measure_msd(pipi_image_t *i1, pipi_image_t *i2)
{
pipi_format_t f1, f2;
double ret = 0.0;
float *p1, *p2;
int x, y, w, h, gray;

w = i1->w < i2->w ? i1->w : i2->w;
h = i1->h < i2->h ? i1->h : i2->h;

f1 = i1->last_modified;
f2 = i2->last_modified;

gray = f1 == PIPI_PIXELS_Y_F32 && f2 == PIPI_PIXELS_Y_F32;

/* FIXME: this is not right */
if(gray)
{
p1 = (float *)i1->p[PIPI_PIXELS_Y_F32].pixels;
p2 = (float *)i2->p[PIPI_PIXELS_Y_F32].pixels;
}
else
{
pipi_get_pixels(i1, PIPI_PIXELS_RGBA_F32);
pipi_get_pixels(i2, PIPI_PIXELS_RGBA_F32);
p1 = (float *)i1->p[PIPI_PIXELS_RGBA_F32].pixels;
p2 = (float *)i2->p[PIPI_PIXELS_RGBA_F32].pixels;
}

if(gray)
{
for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
{
float a = p1[y * i1->w + x];
float b = p2[y * i2->w + x];
ret += (a - b) * (a - b);
}
}
else
{
for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
{
float a, b, sum = 0.0;

a = p1[(y * i1->w + x) * 4];
b = p2[(y * i2->w + x) * 4];
sum += (a - b) * (a - b);

a = p1[(y * i1->w + x) * 4 + 1];
b = p2[(y * i2->w + x) * 4 + 1];
sum += (a - b) * (a - b);

a = p1[(y * i1->w + x) * 4 + 2];
b = p2[(y * i2->w + x) * 4 + 2];
sum += (a - b) * (a - b);

ret += sum / 3;
}
}

/* TODO: free pixels if they were allocated */

/* Restore original image formats */
i1->last_modified = f1;
i2->last_modified = f2;

return ret / (w * h);
}


+ 124
- 0
src/image/codec.cpp Voir le fichier

@@ -0,0 +1,124 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* codec.c: image I/O functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_load(char const *name)
{
pipi_image_t *ret = NULL;

if(!strncmp(name, "random:", 7) ||
!strncmp(name, "ediff:", 6) ||
!strncmp(name, "halftone:", 6) ||
!strncmp(name, "bayer:", 6))
ret = pipi_load_stock(name);

if(!ret)
ret = pipi_load_oric(name);

#if USE_JPEG
if(!ret)
ret = pipi_load_jpeg(name);
#endif
#if USE_IMLIB2
if(!ret)
ret = pipi_load_imlib2(name);
#endif
#if USE_OPENCV
if(!ret)
ret = pipi_load_opencv(name);
#endif
#if USE_SDL
if(!ret)
ret = pipi_load_sdl(name);
#endif
#if USE_GDIPLUS
if(!ret)
ret = pipi_load_gdiplus(name);
#endif
#if USE_GDI
if(!ret)
ret = pipi_load_gdi(name);
#endif
#if USE_COCOA
if(!ret)
ret = pipi_load_coreimage(name);
#endif
return ret;
}

void pipi_free(pipi_image_t *img)
{
int i;

for(i = 0; i < PIPI_PIXELS_MAX; i++)
if(i != img->codec_format && img->p[i].pixels)
free(img->p[i].pixels);

if(img->codec_priv)
img->codec_free(img);

free(img);
}

int pipi_save(pipi_image_t *img, const char *name)
{
int ret = -1;

if(ret < 0)
ret = pipi_save_oric(img, name);

#if USE_JPEG
if(ret < 0)
ret = pipi_save_jpeg(img, name);
#endif
#if USE_IMLIB2
if(ret < 0)
ret = pipi_save_imlib2(img, name);
#endif
#if USE_OPENCV
if(ret < 0)
ret = pipi_save_opencv(img, name);
#endif
#if USE_SDL
if(ret < 0)
ret = pipi_save_sdl(img, name);
#endif
#if USE_GDIPLUS
if(ret < 0)
ret = pipi_save_gdiplus(img, name);
#endif
#if USE_GDI
if(ret < 0)
ret = pipi_save_gdi(img, name);
#endif
#if USE_COCOA
if(ret < 0)
ret = pipi_save_coreimage(img, name);
#endif

return ret;
}


+ 180
- 0
src/image/codec/coreimage.cpp Voir le fichier

@@ -0,0 +1,180 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* coreimage.m: CoreImage (OSX) I/O functions
*/

#import "coreimage.h"

#ifdef USE_COCOA

static int pipi_free_coreimage(pipi_image_t *img);

pipi_image_t *pipi_load_coreimage(const char *name)
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
NSString *n = [NSString stringWithCString: name];
CIImage *source;
NSURL *url = [NSURL fileURLWithPath:n];
source = [CIImage imageWithContentsOfURL:url];
if(source == NULL) return NULL;
CGRect extent = [source extent];
size_t w = (size_t)extent.size.width;
size_t h = (size_t)extent.size.height;


NSBitmapImageRep * myImage;
myImage = [[NSBitmapImageRep alloc] initWithCIImage:source];

pipi_image_t *img;
img = pipi_new(w, h);

img->p[PIPI_PIXELS_RGBA_U8].w = w;
img->p[PIPI_PIXELS_RGBA_U8].h = h;
img->p[PIPI_PIXELS_RGBA_U8].pitch = ([myImage bytesPerRow]/8) * img->w;
img->p[PIPI_PIXELS_RGBA_U8].bpp = [myImage bitsPerPixel];
img->p[PIPI_PIXELS_RGBA_U8].bytes = ([myImage bitsPerPixel]/8) * img->w * img->h;
img->last_modified = PIPI_PIXELS_RGBA_U8;

/* CoreImage feeds us with BGRA while we need RGBA, so convert it.
* We also need to get a pitch==(w*bpp) in order to pipi to opper properly.
*/
int pitch = (img->p[PIPI_PIXELS_RGBA_U8].bpp/8);
unsigned char *tmp = (unsigned char*)malloc(h*w*pitch);
unsigned char *orig = (unsigned char*)[myImage bitmapData];
int x, y, k=0, o=0, a=[myImage bytesPerRow] - (w*([myImage bitsPerPixel]/8));

for(y=0; y<h; y++)
{
for(x=0; x<w*pitch; x+=4)
{
if(!([myImage bitmapFormat] & NSAlphaFirstBitmapFormat))
{
tmp[k+2] = orig[o];
tmp[k+1] = orig[o+1];
tmp[k+0] = orig[o+2];
tmp[k+3] = orig[o+3];
} else
{
tmp[k+0] = orig[o];
tmp[k+1] = orig[o+1];
tmp[k+2] = orig[o+2];
tmp[k+3] = orig[o+3];
}
o+=4;
k+=4;
}
o+=a;
}
img->p[PIPI_PIXELS_RGBA_U8].pixels = tmp;
img->p[PIPI_PIXELS_RGBA_U8].pitch = w*([myImage bitsPerPixel]/8);
img->codec_priv = (struct pipi_codec_coreimage *) malloc(sizeof(struct pipi_codec_coreimage *));
struct pipi_codec_coreimage *infos = (struct pipi_codec_coreimage *) img->codec_priv;
infos->format = [myImage bitmapFormat];

pipi_pixels_t *p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);
img->codec_free = pipi_free_coreimage;
[autoreleasepool release];
return img;
}


int pipi_save_coreimage(pipi_image_t *img, const char *name)
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
pipi_pixels_t *p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);

int i;
char *data = p->pixels;
for(i = 0; i < img->w*img->h; i++)
{
unsigned char r = data[i*4 + 0];
unsigned char g = data[i*4 + 1];
unsigned char b = data[i*4 + 2];
unsigned char a = data[i*4 + 3];
/* R */ data[i*4 + 0] = b;
/* G */ data[i*4 + 1] = g;
/* B */ data[i*4 + 2] = r;
/* A */ data[i*4 + 3] = a;
}
NSString *n = [NSString stringWithCString: name];
NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
pixelsWide:p->w
pixelsHigh:p->h
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSCalibratedRGBColorSpace
bitmapFormat: 0//(NSBitmapFormat)img->codec_priv
bytesPerRow:p->w*4
bitsPerPixel:32
];
if(bitmap == nil) return -1;
memcpy([bitmap bitmapData], data, p->w*p->h*4);

NSBitmapImageFileType type = NSPNGFileType;
if(strlen(name) > 4)
{
char *ext = (char*)&name[strlen(name) - 4];
if( !strncasecmp(ext, ".png", 3)) type = NSPNGFileType;
else if(!strncasecmp(ext, "jpeg", 4)) type = NSJPEGFileType;
else if(!strncasecmp(ext, ".jpg", 3)) type = NSJPEGFileType;
else if(!strncasecmp(ext, ".bmp", 3)) type = NSBMPFileType;
else if(!strncasecmp(ext, ".tif", 3)) type = NSTIFFFileType;
else if(!strncasecmp(ext, ".tiff", 3)) type = NSTIFFFileType;
else if(!strncasecmp(ext, ".gif", 3)) type = NSGIFFileType;
else if(!strncasecmp(ext, ".bmp", 3)) type = NSBMPFileType;
else if(!strncasecmp(ext, ".jp2", 3)) type = NSJPEG2000FileType;
else if(!strncasecmp(ext, ".j2k", 3)) type = NSJPEG2000FileType;
}
[[bitmap representationUsingType:type properties:nil] writeToFile:n atomically:YES];
[autoreleasepool release];

return 1;
}

/*
* XXX: The following functions are local.
*/

static int pipi_free_coreimage(pipi_image_t *img)
{
if(img->codec_priv)
free(img->codec_priv);
return 0;
}

#endif

+ 38
- 0
src/image/codec/coreimage.h Voir le fichier

@@ -0,0 +1,38 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* coreimage.m: CoreImage (OSX) I/O functions
*/

#include "config.h"
#ifdef USE_COCOA

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

#import <Cocoa/Cocoa.h>

#include "pipi.h"
#include "pipi-internals.h"

struct pipi_codec_coreimage
{
NSBitmapFormat format;
};


#endif

+ 148
- 0
src/image/codec/gdi.cpp Voir le fichier

@@ -0,0 +1,148 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* gdi.c: Windows GDI I/O functions (BMP only)
*/

#include "config.h"

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

#include <windows.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_load_gdi(const char *name)
{
BITMAPINFO binfo;
pipi_image_t *img;
pipi_pixels_t *p;
uint8_t *data;
HBITMAP hbmap;
HDC hdc;

hbmap = (HBITMAP)LoadImage(NULL, name, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if(!hbmap)
return NULL;

hdc = CreateCompatibleDC(NULL);
SelectObject(hdc, hbmap);

memset(&binfo, 0, sizeof(binfo));
binfo.bmiHeader.biSize = sizeof(binfo.bmiHeader);
if(!GetDIBits(hdc, hbmap, 0, 0, 0, &binfo, DIB_RGB_COLORS))
{
ReleaseDC(0, hdc);
DeleteObject(hbmap);
return NULL;
}

img = pipi_new(binfo.bmiHeader.biWidth, binfo.bmiHeader.biHeight);
p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);
data = p->pixels;

binfo.bmiHeader.biBitCount = 32;
binfo.bmiHeader.biCompression = BI_RGB;
binfo.bmiHeader.biHeight = - abs(binfo.bmiHeader.biHeight);
if(!GetDIBits(hdc, hbmap, 0, abs(binfo.bmiHeader.biHeight), data,
&binfo, DIB_RGB_COLORS))
{
ReleaseDC(0, hdc);
DeleteObject(hbmap);
return NULL;
}

/* FIXME: do we need to swap bytes? Apparently Vista doesn't need it,
* but we'd need a more thorough test. */

pipi_release_pixels(img, p);

img->codec_priv = NULL;

img->wrap = 0;
img->u8 = 1;

ReleaseDC(0, hdc);
DeleteObject(hbmap);

return img;
}

int pipi_save_gdi(pipi_image_t *img, const char *name)
{
BITMAPINFOHEADER binfo;
BITMAPFILEHEADER bfheader;
uint8_t dummy[4] = { 0 };
HANDLE hfile;
pipi_pixels_t *p;
DWORD ret = 0;
uint8_t *data;
int x, y, padding;

p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);
data = p->pixels;

padding = ((img->w * 3) + 3) / 4 * 4 - img->w * 3;

memset(&binfo, 0, sizeof(binfo));
binfo.biSize = sizeof(binfo);
binfo.biWidth = img->w;
binfo.biHeight = img->h;
binfo.biPlanes = 1;
binfo.biBitCount = 24;
binfo.biCompression = BI_RGB;
binfo.biSizeImage = (img->w * 3 + padding) * img->h;

memset(&bfheader, 0, sizeof(bfheader));
bfheader.bfType = 0x4D42;
bfheader.bfOffBits = sizeof(bfheader) + sizeof(binfo);
bfheader.bfSize = bfheader.bfOffBits + binfo.biSizeImage;

/* We don’t even create the bitmap object, since we know exactly
* what kind of file we are saving. But later, when we support
* different depths and BMP options, we'll need to care about it. */
hfile = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_ARCHIVE, NULL);
if(!hfile)
return -1;
WriteFile(hfile, &bfheader, sizeof(bfheader), &ret, NULL);
WriteFile(hfile, &binfo, sizeof(binfo), &ret, NULL);
for(y = 0; y < img->h; y++)
{
for(x = 0; x < img->w; x++)
{
uint8_t tmp[3];
tmp[0] = data[4 * (img->w * (img->h - 1 - y) + x) + 0];
tmp[1] = data[4 * (img->w * (img->h - 1 - y) + x) + 1];
tmp[2] = data[4 * (img->w * (img->h - 1 - y) + x) + 2];
WriteFile(hfile, tmp, 3, &ret, NULL);
}
if(padding)
WriteFile(hfile, dummy, padding, &ret, NULL);
}
CloseHandle(hfile);

pipi_release_pixels(img, p);

return 0;
}

/*
* XXX: The following functions are local.
*/


+ 120
- 0
src/image/codec/imlib.cpp Voir le fichier

@@ -0,0 +1,120 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* imlib.c: ImLib I/O functions
*/

#include "config.h"

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

#include <Imlib2.h>

#include "pipi.h"
#include "pipi-internals.h"

static int pipi_free_imlib2(pipi_image_t *);

pipi_image_t *pipi_load_imlib2(const char *name)
{
pipi_image_t *img;
Imlib_Image priv = imlib_load_image(name);

if(!priv)
return NULL;

imlib_context_set_image(priv);

if(!imlib_image_get_data())
{
imlib_free_image();
return NULL;
}

img = pipi_new(imlib_image_get_width(), imlib_image_get_height());

img->p[PIPI_PIXELS_RGBA_U8].pixels = imlib_image_get_data();
img->p[PIPI_PIXELS_RGBA_U8].w = img->w;
img->p[PIPI_PIXELS_RGBA_U8].h = img->h;
img->p[PIPI_PIXELS_RGBA_U8].pitch = 4 * img->w;
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32;
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h;
img->last_modified = PIPI_PIXELS_RGBA_U8;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA_U8;
img->codec_free = pipi_free_imlib2;

img->wrap = 0;
img->u8 = 1;

return img;
}

int pipi_save_imlib2(pipi_image_t *img, const char *name)
{
if(!img->codec_priv)
{
Imlib_Image priv = imlib_create_image(img->w, img->h);
void *data;

imlib_context_set_image(priv);
imlib_image_set_has_alpha(1);
data = imlib_image_get_data();

/* FIXME: check pitch differences here */
if(img->last_modified == PIPI_PIXELS_RGBA_U8)
{
memcpy(data, img->p[PIPI_PIXELS_RGBA_U8].pixels,
4 * img->w * img->h);
free(img->p[PIPI_PIXELS_RGBA_U8].pixels);
}

img->p[PIPI_PIXELS_RGBA_U8].pixels = data;
img->p[PIPI_PIXELS_RGBA_U8].w = imlib_image_get_width();
img->p[PIPI_PIXELS_RGBA_U8].h = imlib_image_get_height();
img->p[PIPI_PIXELS_RGBA_U8].pitch = 4 * imlib_image_get_width();
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32;
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA_U8;
img->codec_free = pipi_free_imlib2;

img->wrap = 0;
img->u8 = 1;
}

pipi_set_colorspace(img, img->codec_format);
imlib_context_set_image(img->codec_priv);
imlib_save_image(name);

return 0;
}

/*
* XXX: The following functions are local.
*/

static int pipi_free_imlib2(pipi_image_t *img)
{
imlib_context_set_image(img->codec_priv);
imlib_free_image();

return 0;
}


+ 249
- 0
src/image/codec/jpeg.cpp Voir le fichier

@@ -0,0 +1,249 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* jpeg.c: libjpeg I/O functions
*/

#include "config.h"

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

#include <jpeglib.h>

#include "pipi.h"
#include "pipi-internals.h"

static int pipi_free_jpeg(pipi_image_t *img);

struct my_error_mgr
{
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};

typedef struct my_error_mgr * my_error_ptr;

static void format_msg(j_common_ptr cinfo, char *buf)
{
}

static void emit_msg(j_common_ptr cinfo, int level)
{
}

static void error_msg(j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
cinfo->client_data = (void*)0x1;
longjmp(myerr->setjmp_buffer, 1);
}

static void output_msg(j_common_ptr cinfo)
{
}

pipi_image_t *pipi_load_jpeg(const char *name)
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
unsigned char *image = NULL, *scanline = NULL;
pipi_image_t *img = NULL;
unsigned int i, j, k = 0;
FILE *fp;

fp = fopen(name, "rb");
if(!fp)
return NULL;

if(setjmp(jerr.setjmp_buffer))
goto end;

cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = error_msg;
jerr.pub.emit_message = emit_msg;
jerr.pub.output_message = output_msg;
jerr.pub.format_message = format_msg;

/* Initialize libjpeg */
jpeg_create_decompress(&cinfo);
cinfo.client_data = 0x0;
jpeg_stdio_src(&cinfo, fp);
if(cinfo.client_data == (void *)0x1)
goto end;
jpeg_read_header(&cinfo, TRUE);
if(cinfo.client_data == (void *)0x1)
goto end;
jpeg_start_decompress(&cinfo);
if(cinfo.client_data == (void *)0x1)
goto end;

/* One scanline */
image = malloc(cinfo.output_width * cinfo.output_height * 4);
if(!image)
goto end;

scanline = malloc(cinfo.output_width * 3);

for(i = 0; i < cinfo.output_height; i++)
{
jpeg_read_scanlines(&cinfo, &scanline, 1);
if(cinfo.client_data == (void *)0x1)
{
free(img);
img = NULL;
goto end;
}
for(j = 0; j < cinfo.output_width * 3; j += 3)
{
image[k + 2] = scanline[j];
image[k + 1] = scanline[j + 1];
image[k] = scanline[j + 2];
image[k + 3] = 255;
k += 4;
}
}

img = pipi_new(cinfo.output_width, cinfo.output_height);

img->p[PIPI_PIXELS_RGBA_U8].pixels = image;
img->p[PIPI_PIXELS_RGBA_U8].w = cinfo.output_width;
img->p[PIPI_PIXELS_RGBA_U8].h = cinfo.output_height;
img->p[PIPI_PIXELS_RGBA_U8].pitch = cinfo.output_width * 4;
img->p[PIPI_PIXELS_RGBA_U8].bpp = 24;
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h;
img->last_modified = PIPI_PIXELS_RGBA_U8;

img->codec_priv = (void *)&cinfo;
img->codec_format = PIPI_PIXELS_RGBA_U8;
img->codec_free = pipi_free_jpeg;

img->wrap = 0;
img->u8 = 1;

end:
if(fp)
fclose(fp);
if(scanline)
free(scanline);
jpeg_destroy_decompress(&cinfo);
return img;
}

int pipi_save_jpeg(pipi_image_t *img, const char *name)
{
struct jpeg_compress_struct cinfo;
struct my_error_mgr jerr;
unsigned char *data;
unsigned char *line;
pipi_pixels_t *pixels;
JSAMPROW *jbuf;
uint32_t *ptr;
FILE *fp;
int i, j, y = 0;
size_t len;

int quality = 75; /* FIXME */

len = strlen(name);
if(len < 4 || name[len - 4] != '.'
|| toupper(name[len - 3]) != 'J'
|| toupper(name[len - 2]) != 'P'
|| toupper(name[len - 1]) != 'G')
{
if(len < 5 || name[len - 5] != '.'
|| toupper(name[len - 4]) != 'J'
|| toupper(name[len - 3]) != 'P'
|| toupper(name[len - 2]) != 'E'
|| toupper(name[len - 1]) != 'G')
return -1;
}

pixels = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);
if(!pixels)
return -1;

data = pixels->pixels;

line = malloc(img->w * 3 * sizeof(unsigned char));
if(!line)
return -1;

fp = fopen(name, "wb");
if(!fp)
{
free(line);
return -1;
}

if(setjmp(jerr.setjmp_buffer))
goto end;

jerr.pub.error_exit = error_msg;
jerr.pub.emit_message = emit_msg;
jerr.pub.output_message = output_msg;

cinfo.err = jpeg_std_error(&(jerr.pub));

jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width = img->w;
cinfo.image_height = img->h;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;

jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);

ptr = (uint32_t*)data;

while(cinfo.next_scanline < cinfo.image_height)
{
for (j = 0, i = 0; i < img->w; i++)
{
line[j++] = ((*ptr) >> 16) & 0xff;
line[j++] = ((*ptr) >> 8) & 0xff;
line[j++] = ((*ptr)) & 0xff;
ptr++;
}
jbuf = (JSAMPROW *) (&line);
jpeg_write_scanlines(&cinfo, jbuf, 1);
y++;
}

jpeg_finish_compress(&cinfo);

end:
jpeg_destroy_compress(&cinfo);
free(line);
fclose(fp);

pipi_release_pixels(img, pixels);

return 0;
}

static int pipi_free_jpeg(pipi_image_t *img)
{
if(img->p[PIPI_PIXELS_RGBA_U8].pixels)
free(img->p[PIPI_PIXELS_RGBA_U8].pixels);
return 0;
}


+ 112
- 0
src/image/codec/opencv.cpp Voir le fichier

@@ -0,0 +1,112 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* image.c: image I/O functions
*/

#include "config.h"

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

#include <cv.h>
#include <highgui.h>

#include "pipi.h"
#include "pipi-internals.h"

/* FIXME: this whole file is broken until we support BGR24 images */

static int pipi_free_opencv(pipi_image_t *);

pipi_image_t *pipi_load_opencv(const char *name)
{
pipi_image_t *img;

IplImage *priv = cvLoadImage(name, 1);

if(!priv)
return NULL;

img = pipi_new(priv->width, priv->height);

img->p[PIPI_PIXELS_BGR_U8].pixels = priv->imageData;
img->p[PIPI_PIXELS_BGR_U8].w = priv->width;
img->p[PIPI_PIXELS_BGR_U8].h = priv->height;
img->p[PIPI_PIXELS_BGR_U8].pitch = priv->widthStep;
img->p[PIPI_PIXELS_BGR_U8].bpp = 24;
img->p[PIPI_PIXELS_BGR_U8].bytes = 3 * img->w * img->h;
img->last_modified = PIPI_PIXELS_BGR_U8;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_BGR_U8;
img->codec_free = pipi_free_opencv;

img->wrap = 0;
img->u8 = 1;

return img;
}

int pipi_save_opencv(pipi_image_t *img, const char *name)
{
if(!img->codec_priv)
{
IplImage *priv = cvCreateImage(cvSize(img->w, img->h),
IPL_DEPTH_8U, 3);

/* FIXME: check pitch differences here */
if(img->last_modified == PIPI_PIXELS_BGR_U8)
{
memcpy(priv->imageData, img->p[PIPI_PIXELS_BGR_U8].pixels,
3 * img->w * img->h);
free(img->p[PIPI_PIXELS_BGR_U8].pixels);
}

img->p[PIPI_PIXELS_BGR_U8].pixels = priv->imageData;
img->p[PIPI_PIXELS_BGR_U8].w = priv->width;
img->p[PIPI_PIXELS_BGR_U8].h = priv->height;
img->p[PIPI_PIXELS_BGR_U8].pitch = priv->widthStep;
img->p[PIPI_PIXELS_BGR_U8].bpp = 24;
img->p[PIPI_PIXELS_BGR_U8].bytes = 3 * img->w * img->h;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_BGR_U8;
img->codec_free = pipi_free_opencv;

img->wrap = 0;
img->u8 = 1;
}

pipi_set_colorspace(img, img->codec_format);
cvSaveImage(name, img->codec_priv, NULL);

return 0;
}

/*
* XXX: The following functions are local.
*/

static int pipi_free_opencv(pipi_image_t *img)
{
IplImage *iplimg;
iplimg = (IplImage *)img->codec_priv;
cvReleaseImage(&iplimg);

return 0;
}


+ 588
- 0
src/image/codec/oric.cpp Voir le fichier

@@ -0,0 +1,588 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* oric.c: Oric Atmos import/export functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

/* Image dimensions and recursion depth. DEPTH = 2 is a reasonable value,
* DEPTH = 3 gives good quality, and higher values may improve the results
* even more but at the cost of significantly longer computation times. */
#define WIDTH 240
#define HEIGHT 200
#define DEPTH 2

static int read_screen(char const *name, uint8_t *screen);
static void write_screen(float const *data, uint8_t *screen);

pipi_image_t *pipi_load_oric(char const *name)
{
static uint8_t const pal[32] =
{
0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0xff, 0xff,
0x00, 0xff, 0x00, 0xff,
0x00, 0xff, 0xff, 0xff,
0xff, 0x00, 0x00, 0xff,
0xff, 0x00, 0xff, 0xff,
0xff, 0xff, 0x00, 0xff,
0xff, 0xff, 0xff, 0xff,
};

pipi_image_t *img;
pipi_pixels_t *p;
uint8_t *screen, *data;
int x, y, i;

screen = malloc(WIDTH * HEIGHT / 6);

if(read_screen(name, screen) < 0)
{
free(screen);
return NULL;
}

img = pipi_new(WIDTH, HEIGHT);
p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);
data = p->pixels;

for(y = 0; y < HEIGHT; y++)
{
int bg = 0, fg = 7;

for(x = 0; x < 40; x++)
{
int col;
uint8_t c = screen[y * 40 + x];

if(c & 0x40)
{
for(i = 0; i < 6; i++)
{
col = (c & (1 << (5 - i))) ? (c & 0x80) ? 7 - fg : fg
: (c & 0x80) ? 7 - bg : bg;
memcpy(data + (y * WIDTH + x * 6 + i) * 4,
pal + 4 * col, 4);
}
}
else if((c & 0x60) == 0x00)
{
if(c & 0x10)
bg = c & 0x7;
else
fg = c & 0x7;

col = (c & 0x80) ? 7 - bg : bg;

for(i = 0; i < 6; i++)
memcpy(data + (y * WIDTH + x * 6 + i) * 4,
pal + 4 * col, 4);
}
/* else: invalid sequence */
}
}

free(screen);

img->codec_priv = NULL;

img->wrap = 0;
img->u8 = 1;

pipi_release_pixels(img, p);

return img;
}

int pipi_save_oric(pipi_image_t *img, char const *name)
{
pipi_image_t *tmp = NULL;
pipi_pixels_t *p;
float *data;
uint8_t *screen;
FILE *fp;
size_t len;

len = strlen(name);
if(len < 4 || name[len - 4] != '.'
|| toupper(name[len - 3]) != 'T'
|| toupper(name[len - 2]) != 'A'
|| toupper(name[len - 1]) != 'P')
return -1;

fp = fopen(name, "w");
if(!fp)
return -1;

fwrite("\x16\x16\x16\x16\x24", 1, 5, fp);
fwrite("\x00\xff\x80\x00\xbf\x3f\xa0\x00\x00", 1, 9, fp);
fwrite(name, 1, len - 4, fp);
fwrite("\x00", 1, 1, fp);

if(img->w != WIDTH || img->h != HEIGHT)
tmp = pipi_resize_bicubic(img, WIDTH, HEIGHT);
else
tmp = img;
p = pipi_get_pixels(tmp, PIPI_PIXELS_RGBA_F32);
data = p->pixels;
screen = malloc(WIDTH * HEIGHT / 6);
write_screen(data, screen);
pipi_release_pixels(tmp, p);
if(tmp != img)
pipi_free(tmp);

fwrite(screen, 1, WIDTH * HEIGHT / 6, fp);
fclose(fp);

free(screen);

return 0;
}

/*
* XXX: The following functions are local.
*/

static int read_screen(char const *name, uint8_t *screen)
{
FILE *fp;
int ch;

fp = fopen(name, "r");
if(!fp)
return -1;

/* Skip the sync bytes */
ch = fgetc(fp);
if(ch != 0x16)
goto syntax_error;
while((ch = fgetc(fp)) == 0x16)
;
if(ch != 0x24)
goto syntax_error;

/* Skip the header, ignoring the last byte’s value */
if(fgetc(fp) != 0x00 || fgetc(fp) != 0xff || fgetc(fp) != 0x80
|| fgetc(fp) != 0x00 || fgetc(fp) != 0xbf || fgetc(fp) != 0x3f
|| fgetc(fp) != 0xa0 || fgetc(fp) != 0x00 || fgetc(fp) == EOF)
goto syntax_error;

/* Skip the file name, including trailing nul char */
for(;;)
{
ch = fgetc(fp);
if(ch == EOF)
goto syntax_error;
if(ch == 0x00)
break;
}

/* Read screen data */
if(fread(screen, 1, WIDTH * HEIGHT / 6, fp) != WIDTH * HEIGHT / 6)
goto syntax_error;

fclose(fp);
return 0;

syntax_error:
fclose(fp);
return -1;
}

/* Error diffusion table, similar to Floyd-Steinberg. I choose not to
* propagate 100% of the error, because doing so creates awful artifacts
* (full lines of the same colour, massive colour bleeding) for unclear
* reasons. Atkinson dithering propagates 3/4 of the error, which is even
* less than our 31/32. I also choose to propagate slightly more in the
* X direction to avoid banding effects due to rounding errors.
* It would be interesting, for future versions of this software, to
* propagate the error to the second line, too. But right now I find it far
* too complex to do.
*
* +-------+-------+
* | error |FS0/FSX|
* +-------+-------+-------+
* |FS1/FSX|FS2/FSX|FS3/FSX|
* +-------+-------+-------+
*/
#define FS0 15
#define FS1 6
#define FS2 9
#define FS3 1
#define FSX 32

/* The simple Oric RGB palette, made of the 8 Neugebauer primary colours. Each
* colour is repeated 6 times so that we can point to the palette to paste
* whole blocks of 6 pixels. It’s also organised so that palette[7-x] is the
* RGB negative of palette[x], and screen command X uses palette[X & 7]. */
#define o 0x0000
#define X 0xffff
static const int palette[8][6 * 3] =
{
{ o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o },
{ X, o, o, X, o, o, X, o, o, X, o, o, X, o, o, X, o, o },
{ o, X, o, o, X, o, o, X, o, o, X, o, o, X, o, o, X, o },
{ X, X, o, X, X, o, X, X, o, X, X, o, X, X, o, X, X, o },
{ o, o, X, o, o, X, o, o, X, o, o, X, o, o, X, o, o, X },
{ X, o, X, X, o, X, X, o, X, X, o, X, X, o, X, X, o, X },
{ o, X, X, o, X, X, o, X, X, o, X, X, o, X, X, o, X, X },
{ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X },
};

/* Set new background and foreground colours according to the given command. */
static inline void domove(uint8_t command, uint8_t *bg, uint8_t *fg)
{
if((command & 0x78) == 0x00)
*fg = command & 0x7;
else if((command & 0x78) == 0x10)
*bg = command & 0x7;
}

/* Clamp pixel value to avoid colour bleeding. Deactivated because it
* does not give satisfactory results. */
#define CLAMP 0x1000
static inline int clamp(int p)
{
#if 0
/* FIXME: doesn’t give terribly good results on eg. eatme.png */
if(p < - CLAMP) return - CLAMP;
if(p > 0xffff + CLAMP) return 0xffff + CLAMP;
#endif
return p;
}

/* Compute the perceptual error caused by replacing the input pixels "in"
* with the output pixels "out". "inerr" is the diffused error that should
* be applied to "in"’s first pixel. "outerr" will hold the diffused error
* to apply after "in"’s last pixel upon next call. The return value does
* not mean much physically; it is one part of the algorithm where you need
* to play a bit in order to get appealing results. That’s how image
* processing works, dude. */
static inline int geterror(int const *in, int const *inerr,
int const *out, int *outerr)
{
int tmperr[9 * 3];
int i, c, ret = 0;

/* 9 cells: 1 for the end of line, 8 for the errors below */
memcpy(tmperr, inerr, 3 * sizeof(int));
memset(tmperr + 3, 0, 8 * 3 * sizeof(int));

for(i = 0; i < 6; i++)
{
for(c = 0; c < 3; c++)
{
/* Experiment shows that this is important at small depths */
int a = clamp(in[i * 3 + c] + tmperr[c]);
int b = out[i * 3 + c];
tmperr[c] = (a - b) * FS0 / FSX;
tmperr[c + (i * 3 + 3)] += (a - b) * FS1 / FSX;
tmperr[c + (i * 3 + 6)] += (a - b) * FS2 / FSX;
tmperr[c + (i * 3 + 9)] += (a - b) * FS3 / FSX;
ret += (a - b) / 256 * (a - b) / 256;
}
}

for(i = 0; i < 4; i++)
{
for(c = 0; c < 3; c++)
{
/* Experiment shows that this is important at large depths */
int a = ((in[i * 3 + c] + in[i * 3 + 3 + c]
+ in[i * 3 + 6 + c]) / 3);
int b = ((out[i * 3 + c] + out[i * 3 + 3 + c]
+ out[i * 3 + 6 + c]) / 3);
ret += (a - b) / 256 * (a - b) / 256;
}
}

/* Using the diffused error as a perceptual error component is stupid,
* because that’s not what it is at all, but I found that it helped a
* bit in some cases. */
for(i = 0; i < 3; i++)
ret += tmperr[i] / 256 * tmperr[i] / 256;

memcpy(outerr, tmperr, 3 * sizeof(int));

return ret;
}

static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg,
int const *errvec, int depth, int maxerror,
int *error, int *out)
{
int voidvec[3], nvoidvec[3], bestrgb[6 * 3], tmprgb[6 * 3], tmpvec[3];
int const *voidrgb, *nvoidrgb, *vec, *rgb;
int besterror, curerror, suberror, statice, voide, nvoide;
int i, j, c;
uint8_t command, bestcommand;

/* Precompute error for the case where we change the foreground colour
* and hence only print the background colour or its negative */
voidrgb = palette[bg];
voide = geterror(in, errvec, voidrgb, voidvec);
nvoidrgb = palette[7 - bg];
nvoide = geterror(in, errvec, nvoidrgb, nvoidvec);

/* Precompute sub-error for the case where we print pixels (and hence
* don’t change the palette). It’s not the exact error because we should
* be propagating the error to the first pixel here. */
if(depth > 0)
{
int tmp[3] = { 0, 0, 0 };
bestmove(in + 6 * 3, bg, fg, tmp, depth - 1, maxerror, &statice, NULL);
}

/* Check every likely command:
* 0-7: change foreground to 0-7
* 8-15: change foreground to 0-7, print negative background
* 16-23: change background to 0-7
* 24-31: change background to 0-7, print negative background
* 32: normal stuff
* 33: inverse video stuff */
besterror = 0x7ffffff;
bestcommand = 0x10;
memcpy(bestrgb, voidrgb, 6 * 3 * sizeof(int));
for(j = 0; j < 34; j++)
{
static uint8_t const lookup[] =
{
0x00, 0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07,
0x80, 0x84, 0x81, 0x85, 0x82, 0x86, 0x83, 0x87,
0x10, 0x14, 0x11, 0x15, 0x12, 0x16, 0x13, 0x17,
0x90, 0x94, 0x91, 0x95, 0x92, 0x96, 0x93, 0x97,
0x40, 0xc0
};

uint8_t newbg = bg, newfg = fg;

command = lookup[j];
domove(command, &newbg, &newfg);

/* Keeping bg and fg is useless, because we could use standard
* pixel printing instead */
if((command & 0x40) == 0x00 && newbg == bg && newfg == fg)
continue;

/* I *think* having newfg == newbg is useless, too, but I don’t
* want to miss some corner case where swapping bg and fg may be
* interesting, so we continue anyway. */

#if 0
/* Bit 6 off and bit 5 on seems illegal */
if((command & 0x60) == 0x20)
continue;

/* Bits 6 and 5 off and bit 3 on seems illegal */
if((command & 0x68) == 0x08)
continue;
#endif

if((command & 0xf8) == 0x00)
{
curerror = voide;
rgb = voidrgb;
vec = voidvec;
}
else if((command & 0xf8) == 0x80)
{
curerror = nvoide;
rgb = nvoidrgb;
vec = nvoidvec;
}
else if((command & 0xf8) == 0x10)
{
rgb = palette[newbg];
curerror = geterror(in, errvec, rgb, tmpvec);
vec = tmpvec;
}
else if((command & 0xf8) == 0x90)
{
rgb = palette[7 - newbg];
curerror = geterror(in, errvec, rgb, tmpvec);
vec = tmpvec;
}
else
{
int const *bgcolor, *fgcolor;

if((command & 0x80) == 0x00)
{
bgcolor = palette[bg]; fgcolor = palette[fg];
}
else
{
bgcolor = palette[7 - bg]; fgcolor = palette[7 - fg];
}

memcpy(tmpvec, errvec, 3 * sizeof(int));
curerror = 0;

for(i = 0; i < 6; i++)
{
int vec1[3], vec2[3];
int smalle1 = 0, smalle2 = 0;

memcpy(vec1, tmpvec, 3 * sizeof(int));
memcpy(vec2, tmpvec, 3 * sizeof(int));
for(c = 0; c < 3; c++)
{
int delta1, delta2;
delta1 = clamp(in[i * 3 + c] + tmpvec[c]) - bgcolor[c];
vec1[c] = delta1 * FS0 / FSX;
smalle1 += delta1 / 256 * delta1;
delta2 = clamp(in[i * 3 + c] + tmpvec[c]) - fgcolor[c];
vec2[c] = delta2 * FS0 / FSX;
smalle2 += delta2 / 256 * delta2;
}

if(smalle1 < smalle2)
{
memcpy(tmpvec, vec1, 3 * sizeof(int));
memcpy(tmprgb + i * 3, bgcolor, 3 * sizeof(int));
}
else
{
memcpy(tmpvec, vec2, 3 * sizeof(int));
memcpy(tmprgb + i * 3, fgcolor, 3 * sizeof(int));
command |= (1 << (5 - i));
}
}

/* Recompute full error */
curerror += geterror(in, errvec, tmprgb, tmpvec);

rgb = tmprgb;
vec = tmpvec;
}

if(curerror > besterror)
continue;

/* Try to avoid bad decisions now that will have a high cost
* later in the line by making the next error more important than
* the current error. */
curerror = curerror * 3 / 4;

if(depth == 0)
suberror = 0; /* It’s the end of the tree */
else if((command & 0x68) == 0x00)
{
bestmove(in + 6 * 3, newbg, newfg, vec, depth - 1,
besterror - curerror, &suberror, NULL);

#if 0
/* Slight penalty for colour changes; they're hard to revert. The
* value of 2 was determined empirically. 1.5 is not enough and
* 3 is too much. */
if(newbg != bg)
suberror = suberror * 10 / 8;
else if(newfg != fg)
suberror = suberror * 9 / 8;
#endif
}
else
suberror = statice;

if(curerror + suberror < besterror)
{
besterror = curerror + suberror;
bestcommand = command;
memcpy(bestrgb, rgb, 6 * 3 * sizeof(int));
}
}

*error = besterror;
if(out)
memcpy(out, bestrgb, 6 * 3 * sizeof(int));

return bestcommand;
}

static void write_screen(float const *data, uint8_t *screen)
{
int *src, *srcl, *dst, *dstl;
int stride, x, y, depth, c;

stride = (WIDTH + 1) * 3;

src = malloc((WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int));
memset(src, 0, (WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int));

dst = malloc((WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int));
memset(dst, 0, (WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int));

/* Import pixels into our custom format */
for(y = 0; y < HEIGHT; y++)
for(x = 0; x < WIDTH; x++)
for(c = 0; c < 3; c++)
src[y * stride + x * 3 + c] =
0xffff * data[(y * WIDTH + x) * 4 + (2 - c)];

/* Let the fun begin */
for(y = 0; y < HEIGHT; y++)
{
uint8_t bg = 0, fg = 7;

//fprintf(stderr, "\rProcessing... %i%%", (y * 100 + 99) / HEIGHT);

for(x = 0; x < WIDTH; x += 6)
{
int errvec[3] = { 0, 0, 0 };
int dummy, i;
uint8_t command;

depth = (x + DEPTH < WIDTH) ? DEPTH : (WIDTH - x) / 6 - 1;
srcl = src + y * stride + x * 3;
dstl = dst + y * stride + x * 3;

/* Recursively compute and apply best command */
command = bestmove(srcl, bg, fg, errvec, depth, 0x7fffff,
&dummy, dstl);
/* Propagate error */
for(c = 0; c < 3; c++)
{
for(i = 0; i < 6; i++)
{
int error = srcl[i * 3 + c] - dstl[i * 3 + c];
srcl[i * 3 + c + 3] =
clamp(srcl[i * 3 + c + 3] + error * FS0 / FSX);
srcl[i * 3 + c + stride - 3] += error * FS1 / FSX;
srcl[i * 3 + c + stride] += error * FS2 / FSX;
srcl[i * 3 + c + stride + 3] += error * FS3 / FSX;
}

for(i = -1; i < 7; i++)
srcl[i * 3 + c + stride] = clamp(srcl[i * 3 + c + stride]);
}
/* Iterate */
domove(command, &bg, &fg);
/* Write byte to file */
screen[y * (WIDTH / 6) + (x / 6)] = command;
}
}

//fprintf(stderr, " done.\n");
}


+ 133
- 0
src/image/codec/sdl.cpp Voir le fichier

@@ -0,0 +1,133 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* sdl.c: SDL_image I/O functions
*/

#include "config.h"

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

#include <SDL_image.h>

#include "pipi.h"
#include "pipi-internals.h"

static int pipi_free_sdl(pipi_image_t *);
static SDL_Surface *create_32bpp_surface(int w, int h);

pipi_image_t *pipi_load_sdl(const char *name)
{
pipi_image_t *img;
SDL_Surface *priv = IMG_Load(name);

if(!priv)
return NULL;

if(priv->format->BytesPerPixel != 4)
{
SDL_Surface *tmp = create_32bpp_surface(priv->w, priv->h);
SDL_BlitSurface(priv, NULL, tmp, NULL);
SDL_FreeSurface(priv);
priv = tmp;
}

img = pipi_new(priv->w, priv->h);

img->p[PIPI_PIXELS_RGBA_U8].pixels = priv->pixels;
img->p[PIPI_PIXELS_RGBA_U8].w = priv->w;
img->p[PIPI_PIXELS_RGBA_U8].h = priv->h;
img->p[PIPI_PIXELS_RGBA_U8].pitch = priv->pitch;
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32;
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h;
img->last_modified = PIPI_PIXELS_RGBA_U8;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA_U8;
img->codec_free = pipi_free_sdl;

img->wrap = 0;
img->u8 = 1;

return img;
}

int pipi_save_sdl(pipi_image_t *img, const char *name)
{
if(!img->codec_priv)
{
SDL_Surface *priv = create_32bpp_surface(img->w, img->h);

/* FIXME: check pitch differences here */
if(img->last_modified == PIPI_PIXELS_RGBA_U8)
{
memcpy(priv->pixels, img->p[PIPI_PIXELS_RGBA_U8].pixels,
priv->pitch * priv->h);
free(img->p[PIPI_PIXELS_RGBA_U8].pixels);
}

img->p[PIPI_PIXELS_RGBA_U8].pixels = priv->pixels;
img->p[PIPI_PIXELS_RGBA_U8].w = priv->w;
img->p[PIPI_PIXELS_RGBA_U8].h = priv->h;
img->p[PIPI_PIXELS_RGBA_U8].pitch = priv->pitch;
img->p[PIPI_PIXELS_RGBA_U8].bpp = 32;
img->p[PIPI_PIXELS_RGBA_U8].bytes = 4 * img->w * img->h;

img->codec_priv = (void *)priv;
img->codec_format = PIPI_PIXELS_RGBA_U8;
img->codec_free = pipi_free_sdl;

img->wrap = 0;
img->u8 = 1;
}

pipi_set_colorspace(img, img->codec_format);
SDL_SaveBMP(img->codec_priv, name);

return 0;
}

/*
* XXX: The following functions are local.
*/

static int pipi_free_sdl(pipi_image_t *img)
{
SDL_FreeSurface(img->codec_priv);

return 0;
}

static SDL_Surface *create_32bpp_surface(int w, int h)
{
Uint32 rmask, gmask, bmask, amask;
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x00000000;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0x00000000;
#endif

return SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
rmask, gmask, bmask, amask);
}


+ 127
- 0
src/image/colorstring.cpp Voir le fichier

@@ -0,0 +1,127 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* colorstring.c: color from string functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"


struct color_table
{
char name[255];
float a,r,g,b;
};

struct color_table color_table[] =
{
{ "black" , 1, 0, 0, 0 },
{ "white" , 1, 1, 1, 1 },
{ "red" , 1, 1, 0, 0 },
{ "green" , 1, 0, 1, 0 },
{ "blue" , 1, 0, 0, 1 },
{ "yellow" , 1, 1, 1, 0 },
{ "cyan" , 1, 0, 1, 1 },
{ "magenta", 1, 1, 0, 1 },
{ "grey" , 1, 0.5, 0.5, 0.5 },
{ "gray" , 1, 0.5, 0.5, 0.5 },
{ "grey50" , 1, 0.5, 0.5, 0.5 },
{ "gray50" , 1, 0.5, 0.5, 0.5 },
{ "grey25" , 1, 0.25, 0.25, 0.25 },
{ "gray25" , 1, 0.25, 0.25, 0.25 },
};



pipi_pixel_t *pipi_get_color_from_string(const char* s)
{
pipi_pixel_t *color;

if(!s) return NULL;

color = malloc(sizeof(pipi_pixel_t));

if(s[0] == '#')
{
uint32_t c = 0;
sscanf(s, "%x", &c);

color->pixel_float.a = ((c&0xFF000000)>>24) / 255.0f;
color->pixel_float.r = ((c&0x00FF0000)>>16) / 255.0f;
color->pixel_float.g = ((c&0x0000FF00)>>8) / 255.0f;
color->pixel_float.b = ((c&0x000000FF)>>0) / 255.0f;
}
else if(!strncmp(s, "rgb(", 4))
{
uint32_t r ,g ,b;
sscanf(s, "rgb(%u,%u,%u)", &r, &g, &b);
color->pixel_float.r = r / 255.0f;
color->pixel_float.g = g / 255.0f;
color->pixel_float.b = b / 255.0f;
}
else if(!strncmp(s, "frgb(", 5))
{
float r ,g ,b;
sscanf(s, "frgb(%f,%f,%f)", &r, &g, &b);
color->pixel_float.r = r;
color->pixel_float.g = g;
color->pixel_float.b = b;
}
else if(!strncmp(s, "argb(", 4))
{
uint32_t a, r ,g ,b;
sscanf(s, "argb(%u,%u,%u,%u)", &a, &r, &g, &b);
color->pixel_float.a = a / 255.0f;
color->pixel_float.r = r / 255.0f;
color->pixel_float.g = g / 255.0f;
color->pixel_float.b = b / 255.0f;
}
else if(!strncmp(s, "fargb(", 5))
{
float a, r ,g ,b;
sscanf(s, "fargb(%f, %f,%f,%f)", &a, &r, &g, &b);
color->pixel_float.a = a;
color->pixel_float.r = r;
color->pixel_float.g = g;
color->pixel_float.b = b;
}
else
{
unsigned int i;
for(i=0; i<sizeof(color_table); i++)
{
if(!strcasecmp(color_table[i].name,s))
{
color->pixel_float.a = color_table[i].a;
color->pixel_float.r = color_table[i].r;
color->pixel_float.g = color_table[i].g;
color->pixel_float.b = color_table[i].b;
break;
}
}


}
return color;
}

+ 89
- 0
src/image/combine/blit.cpp Voir le fichier

@@ -0,0 +1,89 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* mean.c: Mean computation function
*/

#include "config.h"

#include <stdlib.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_blit(pipi_image_t *img1, pipi_image_t *img2, int x, int y)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int dx, dy, w1, h1, w2, h2;

w1 = img1->w;
h1 = img1->h;
w2 = img2->w;
h2 = img2->h;

dst = pipi_copy(img1);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(dy = 0; dy < h2; dy++)
{
if (y + dy < 0)
continue;

if (y + dy >= h1)
break;

for(dx = 0; dx < w2; dx++)
{
float p, q;
double t1, t2;

if (x + dx < 0)
continue;

if (x + dx >= w1)
break;

t1 = img2data[4 * (dy * w2 + dx) + 3];
t2 = 1.0 - t1;

p = img1data[4 * ((y + dy) * w1 + (x + dx))];
q = img2data[4 * (dy * w2 + dx)];
dstdata[4 * ((y + dy) * w1 + (x + dx))] = t2 * p + t1 * q;

p = img1data[4 * ((y + dy) * w1 + (x + dx)) + 1];
q = img2data[4 * (dy * w2 + dx) + 1];
dstdata[4 * ((y + dy) * w1 + (x + dx)) + 1] = t2 * p + t1 * q;

p = img1data[4 * ((y + dy) * w1 + (x + dx)) + 2];
q = img2data[4 * (dy * w2 + dx) + 2];
dstdata[4 * ((y + dy) * w1 + (x + dx)) + 2] = t2 * p + t1 * q;

p = img1data[4 * ((y + dy) * w1 + (x + dx)) + 3];
q = img2data[4 * (dy * w2 + dx) + 3];
dstdata[4 * ((y + dy) * w1 + (x + dx)) + 3] = t2 * p + t1 * q;
}
}

return dst;
}


+ 86
- 0
src/image/combine/merge.cpp Voir le fichier

@@ -0,0 +1,86 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* mean.c: Mean computation function
*/

#include "config.h"

#include <stdlib.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_merge(pipi_image_t *img1, pipi_image_t *img2, double t)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

if(t < 0.0)
t = 0.0;
else if(t > 1.0)
t = 1.0;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;
double t1 = t * img2data[4 * (y * w + x) + 3];
double t2 = 1.0 - t1;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = t2 * p + t1 * q;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = t2 * p + t1 * q;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = t2 * p + t1 * q;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = t2 * p + t1 * q;
}
}

return dst;
}

pipi_image_t *pipi_mean(pipi_image_t *img1, pipi_image_t *img2)
{
return pipi_merge(img1, img2, 0.5);
}


+ 123
- 0
src/image/combine/minmax.cpp Voir le fichier

@@ -0,0 +1,123 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* minmax.c: min and max computation functions
*/

#include "config.h"

#include <stdlib.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_min(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = p < q ? p : q;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = p < q ? p : q;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = p < q ? p : q;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = p < q ? p : q;
}
}

return dst;
}

pipi_image_t *pipi_max(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = p > q ? p : q;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = p > q ? p : q;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = p > q ? p : q;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = p > q ? p : q;
}
}

return dst;
}


+ 221
- 0
src/image/combine/mulscreen.cpp Voir le fichier

@@ -0,0 +1,221 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* mulscreen.c: multiply and screen computation functions
*/

#include "config.h"

#include <stdlib.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_multiply(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = p * q;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = p * q;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = p * q;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = p * q;
}
}

return dst;
}

pipi_image_t *pipi_divide(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = p >= q ? 1. : p / q;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = p >= q ? 1. : p / q;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = p >= q ? 1. : p / q;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = p >= q ? 1. : p / q;
}
}

return dst;
}

pipi_image_t *pipi_screen(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = p + q - p * q;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = p + q - p * q;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = p + q - p * q;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = p + q - p * q;
}
}

return dst;
}

pipi_image_t *pipi_overlay(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = p * (p + 2. * q * (1. - p));

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = p * (p + 2. * q * (1. - p));

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = p * (p + 2. * q * (1. - p));

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = p * (p + 2. * q * (1. - p));
}
}

return dst;
}


+ 135
- 0
src/image/combine/rgb.cpp Voir le fichier

@@ -0,0 +1,135 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* rgb.c: RGB combining function
*/

#include "config.h"

#include <stdlib.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_rgb(pipi_image_t *i1, pipi_image_t *i2, pipi_image_t *i3)
{
pipi_image_t *dst;
pipi_pixels_t *i1p, *i2p, *i3p, *dstp;
float *i1data, *i2data, *i3data, *dstdata;
int x, y, w, h;

if(i1->w != i2->w || i1->h != i2->h || i1->w != i3->w || i1->h != i3->h)
return NULL;

w = i1->w;
h = i1->h;

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

i1p = pipi_get_pixels(i1, PIPI_PIXELS_Y_F32);
i1data = (float *)i1p->pixels;
i2p = pipi_get_pixels(i2, PIPI_PIXELS_Y_F32);
i2data = (float *)i2p->pixels;
i3p = pipi_get_pixels(i3, PIPI_PIXELS_Y_F32);
i3data = (float *)i3p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
dstdata[4 * (y * w + x)] = i1data[y * w + x];
dstdata[4 * (y * w + x) + 1] = i2data[y * w + x];
dstdata[4 * (y * w + x) + 2] = i3data[y * w + x];
dstdata[4 * (y * w + x) + 3] = 1.0;
}
}

return dst;
}

pipi_image_t *pipi_red(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h;

w = src->w;
h = src->h;

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

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
dstdata[y * w + x] = srcdata[4 * (y * w + x)];

return dst;
}

pipi_image_t *pipi_green(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h;

w = src->w;
h = src->h;

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

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
dstdata[y * w + x] = srcdata[4 * (y * w + x) + 1];

return dst;
}

pipi_image_t *pipi_blue(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h;

w = src->w;
h = src->h;

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

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
dstdata[y * w + x] = srcdata[4 * (y * w + x) + 2];

return dst;
}


+ 173
- 0
src/image/combine/subadd.cpp Voir le fichier

@@ -0,0 +1,173 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* subadd.c: sub, add and difference computation functions
*/

#include "config.h"

#include <stdlib.h>
#include <math.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_add(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = (p + q < 1.) ? p + q : 1.;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = (p + q < 1.) ? p + q : 1.;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = (p + q < 1.) ? p + q : 1.;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = (p + q < 1.) ? p + q : 1.;
}
}

return dst;
}

pipi_image_t *pipi_sub(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = p < q ? 0 : p - q;

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = p < q ? 0 : p - q;

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = p < q ? 0 : p - q;

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = p < q ? 0 : p - q;
}
}

return dst;
}

pipi_image_t *pipi_difference(pipi_image_t *img1, pipi_image_t *img2)
{
pipi_image_t *dst;
pipi_pixels_t *img1p, *img2p, *dstp;
float *img1data, *img2data, *dstdata;
int x, y, w, h;

if(img1->w != img2->w || img1->h != img2->h)
return NULL;

w = img1->w;
h = img1->h;

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

img1p = pipi_get_pixels(img1, PIPI_PIXELS_RGBA_F32);
img1data = (float *)img1p->pixels;
img2p = pipi_get_pixels(img2, PIPI_PIXELS_RGBA_F32);
img2data = (float *)img2p->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

p = img1data[4 * (y * w + x)];
q = img2data[4 * (y * w + x)];
dstdata[4 * (y * w + x)] = fabsf(p - q);

p = img1data[4 * (y * w + x) + 1];
q = img2data[4 * (y * w + x) + 1];
dstdata[4 * (y * w + x) + 1] = fabsf(p - q);

p = img1data[4 * (y * w + x) + 2];
q = img2data[4 * (y * w + x) + 2];
dstdata[4 * (y * w + x) + 2] = fabsf(p - q);

p = img1data[4 * (y * w + x) + 3];
q = img2data[4 * (y * w + x) + 3];
dstdata[4 * (y * w + x) + 3] = fabsf(p - q);
}
}

return dst;
}


+ 736
- 0
src/image/context.cpp Voir le fichier

@@ -0,0 +1,736 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* context.c: processing stack handling routines
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_context_t *pipi_create_context()
{
pipi_context_t *ret;

ret = malloc(sizeof(pipi_context_t));
memset(ret, 0, sizeof(pipi_context_t));

return ret;
}

void pipi_destroy_context(pipi_context_t *ctx)
{
free(ctx);
}

pipi_command_t const *pipi_get_command_list(void)
{
static pipi_command_t const list[] =
{
{ "load", 1 },
{ "save", 1 },

{ "dup", 0 },
{ "swap", 0 },
{ "roll", 1 },

{ "gamma", 1 },
{ "scale", 1 },
{ "crop", 1 },
{ "geometry", 1 },
{ "tile", 1 },
{ "dither", 1 },
{ "blur", 1 },
{ "boxblur", 1 },
{ "median", 1 },
{ "gray", 0 },
{ "brightness", 1 },
{ "contrast", 1 },
{ "autocontrast", 0 },
{ "order", 0 },
{ "hflip", 0 },
{ "vflip", 0 },
{ "rotate90", 0 },
{ "rotate180", 0 },
{ "rotate270", 0 },
{ "rotate", 1 },
{ "invert", 0 },
{ "threshold", 1 },
{ "dilate", 0 },
{ "erode", 0 },
{ "wrap", 0 },
{ "combine", 0 },
{ "split", 0 },
{ "blit", 1 },
{ "mean", 0 },
{ "merge", 1 },
{ "min", 0 },
{ "max", 0 },
{ "add", 0 },
{ "sub", 0 },
{ "difference", 0 },
{ "multiply", 0 },
{ "divide", 0 },
{ "screen", 0 },
{ "overlay", 0 },
{ "line", 1 },
{ "sine", 1 },
{ "wave", 1 },
{ "rgb2yuv", 0 },
{ "yuv2rgb", 0 },

/* End marker */
{ NULL, 0 }
};

return list;
}

int pipi_command(pipi_context_t *ctx, char const *cmd, ...)
{
if(!strcmp(cmd, "load"))
{
char const *file;
va_list ap;

va_start(ap, cmd);
file = va_arg(ap, char const *);
va_end(ap);
ctx->images[ctx->nimages] = pipi_load(file);
if(ctx->images[ctx->nimages] == NULL)
return -1;
ctx->nimages++;
}
else if(!strcmp(cmd, "save"))
{
char const *file;
va_list ap;

if(ctx->nimages < 1)
return -1;
ctx->nimages--;
va_start(ap, cmd);
file = va_arg(ap, char const *);
va_end(ap);
pipi_save(ctx->images[ctx->nimages], file);
pipi_free(ctx->images[ctx->nimages]);
}
else if(!strcmp(cmd, "gamma"))
{
char const *val;
va_list ap;

va_start(ap, cmd);
val = va_arg(ap, char const *);
va_end(ap);

pipi_set_gamma(atof(val));
}
else if(!strcmp(cmd, "dither"))
{
pipi_image_t *src, *dst;
char const *method;
va_list ap;

if(ctx->nimages < 1)
return -1;
va_start(ap, cmd);
method = va_arg(ap, char const *);
va_end(ap);
src = ctx->images[ctx->nimages - 1];
dst = NULL;
if(!strcmp(method, "ost"))
dst = pipi_dither_ostromoukhov(src, 0);
else if(!strcmp(method, "sost"))
dst = pipi_dither_ostromoukhov(src, 1);
else if(!strcmp(method, "ediff"))
{
if(ctx->nimages < 2)
return -1;
dst = pipi_dither_ediff(ctx->images[ctx->nimages - 2], src, 0);
pipi_free(ctx->images[ctx->nimages - 2]);
ctx->nimages--;
}
else if(!strcmp(method, "sediff"))
{
if(ctx->nimages < 2)
return -1;
dst = pipi_dither_ediff(ctx->images[ctx->nimages - 2], src, 1);
pipi_free(ctx->images[ctx->nimages - 2]);
ctx->nimages--;
}
else if(!strncmp(method, "ordered", 7))
{
double scale = 1., angle = .0;
if(ctx->nimages < 2)
return -1;
method = strchr(method, ':');
if(method)
{
scale = atof(method + 1);
method = strchr(method + 1, ':');
if(method)
angle = atof(method + 1);
}
if(scale <= 0.)
scale = 1.;
dst = pipi_dither_ordered_ext(ctx->images[ctx->nimages - 2], src,
scale, angle);
pipi_free(ctx->images[ctx->nimages - 2]);
ctx->nimages--;
}
else if(!strncmp(method, "halftone", 8))
{
double r, angle = .0;
method = strchr(method, ':');
if(!method)
return -1;
r = atof(method + 1);
method = strchr(method + 1, ':');
if(method)
angle = atof(method + 1);
if(r < 1.)
r = 1.;
dst = pipi_dither_halftone(src, r, angle);
}
else if(!strcmp(method, "random"))
dst = pipi_dither_random(src);
else if(!strcmp(method, "dbs"))
dst = pipi_dither_dbs(src);
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else if(!strcmp(cmd, "blur"))
{
pipi_image_t *src, *dst;
char const *arg;
va_list ap;
double w, h, a = 0.0;

if(ctx->nimages < 1)
return -1;
va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
w = h = atof(arg);
arg = strchr(arg, 'x');
if(arg)
{
h = atof(arg + 1);
arg = strchr(arg, 'r');
if(arg)
a = atof(arg + 1);
}
src = ctx->images[ctx->nimages - 1];
dst = pipi_gaussian_blur_ext(src, w, h, a, 0.0, 0.0);
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else if(!strcmp(cmd, "boxblur") || !strcmp(cmd, "median"))
{
pipi_image_t *src, *dst = NULL;
char const *arg;
va_list ap;
double w, h;

if(ctx->nimages < 1)
return -1;
va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
w = h = atof(arg);
arg = strchr(arg, 'x');
if(arg)
h = atof(arg + 1);
src = ctx->images[ctx->nimages - 1];
switch(cmd[0])
{
case 'b': dst = pipi_box_blur_ext(src, w, h); break;
case 'm': dst = pipi_median_ext(src, w, h); break;
}
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else if(!strcmp(cmd, "geometry") || !strcmp(cmd, "tile"))
{
pipi_image_t *src, *dst = NULL;
char const *arg;
va_list ap;
int w, h;

if(ctx->nimages < 1)
return -1;
va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
w = atoi(arg);
arg = strchr(arg, 'x');
if(!arg)
return -1;
h = atoi(arg + 1);
if(w <= 0 || h <= 0)
return -1;
src = ctx->images[ctx->nimages - 1];
switch(cmd[0])
{
case 'g': dst = pipi_resize_bicubic(src, w, h); break;
case 't': dst = pipi_tile(src, w, h); break;
}
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else if(!strcmp(cmd, "scale"))
{
pipi_image_t *src, *dst;
char const *arg;
va_list ap;
double scale;
int w, h;

if(ctx->nimages < 1)
return -1;
src = ctx->images[ctx->nimages - 1];
va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
scale = atof(arg);
w = (int)(scale * src->w + 0.5);
h = (int)(scale * src->h + 0.5);
if(w <= 0 || h <= 0)
return -1;
dst = pipi_resize_bicubic(src, w, h);
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else if(!strcmp(cmd, "crop"))
{
pipi_image_t *tmp;
char const *arg;
va_list ap;
int w, h, x = 0, y = 0;
int ret;

if(ctx->nimages < 1)
return -1;

va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);

ret = sscanf(arg, "%dx%d+%d+%d", &w, &h, &x, &y);
if(ret < 2)
return -1;

tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_crop(tmp, w, h, x, y);
pipi_free(tmp);
}
else if(!strcmp(cmd, "brightness") || !strcmp(cmd, "contrast")
|| !strcmp(cmd, "threshold") || !strcmp(cmd, "rotate"))
{
pipi_image_t *src, *dst = NULL;
char const *arg;
va_list ap;
double val;

if(ctx->nimages < 1)
return -1;
va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
val = atof(arg);
src = ctx->images[ctx->nimages - 1];
switch(cmd[0])
{
case 'b': dst = pipi_brightness(src, val); break;
case 'c': dst = pipi_contrast(src, val); break;
case 'r': dst = pipi_rotate(src, val); break;
case 't': dst = pipi_threshold(src, val); break;
}
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else if(!strcmp(cmd, "hflip"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_hflip(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "vflip"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_vflip(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "rotate90"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_rotate90(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "rotate180"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_rotate180(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "rotate270"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_rotate270(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "order"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_order(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "split"))
{
pipi_image_t *src;

if(ctx->nimages < 1)
return -1;
src = ctx->images[ctx->nimages - 1];
ctx->nimages += 2;
ctx->images[ctx->nimages - 3] = pipi_red(src);
ctx->images[ctx->nimages - 2] = pipi_green(src);
ctx->images[ctx->nimages - 1] = pipi_blue(src);
pipi_free(src);
}
else if(!strcmp(cmd, "combine"))
{
pipi_image_t *dst;

if(ctx->nimages < 3)
return -1;
dst = pipi_rgb(ctx->images[ctx->nimages - 3],
ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
if(dst == NULL)
return -1;
pipi_free(ctx->images[ctx->nimages - 3]);
pipi_free(ctx->images[ctx->nimages - 2]);
pipi_free(ctx->images[ctx->nimages - 1]);
ctx->images[ctx->nimages - 3] = dst;
ctx->nimages -= 2;
}
else if(!strcmp(cmd, "blit"))
{
pipi_image_t *dst;
char const *arg;
va_list ap;
int x, y;

if(ctx->nimages < 2)
return -1;
va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
x = atoi(arg);
arg = strchr(arg, 'x');
if(!arg)
return -1;
y = atoi(arg + 1);

dst = pipi_blit(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1], x, y);
if(dst == NULL)
return -1;
pipi_free(ctx->images[ctx->nimages - 2]);
pipi_free(ctx->images[ctx->nimages - 1]);
ctx->images[ctx->nimages - 2] = dst;
ctx->nimages--;
}
else if(!strcmp(cmd, "merge"))
{
pipi_image_t *dst;
char const *arg;
va_list ap;
double val;

if(ctx->nimages < 2)
return -1;

va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
val = atof(arg);

dst = pipi_merge(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1], val);
if(dst == NULL)
return -1;
pipi_free(ctx->images[ctx->nimages - 2]);
pipi_free(ctx->images[ctx->nimages - 1]);
ctx->images[ctx->nimages - 2] = dst;
ctx->nimages--;
}
else if(!strcmp(cmd, "mean") || !strcmp(cmd, "min") || !strcmp(cmd, "max")
|| !strcmp(cmd, "add") || !strcmp(cmd, "sub")
|| !strcmp(cmd, "difference") || !strcmp(cmd, "multiply")
|| !strcmp(cmd, "divide") || !strcmp(cmd, "screen")
|| !strcmp(cmd, "overlay"))
{
pipi_image_t *dst = NULL;

if(ctx->nimages < 2)
return -1;
switch(cmd[2])
{
case 'a': dst = pipi_mean(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'n': dst = pipi_min(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'x': dst = pipi_max(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'd': dst = pipi_add(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'b': dst = pipi_sub(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'f': dst = pipi_difference(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'l': dst = pipi_multiply(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'v': dst = pipi_divide(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'r': dst = pipi_screen(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
case 'e': dst = pipi_overlay(ctx->images[ctx->nimages - 2],
ctx->images[ctx->nimages - 1]);
break;
}
if(dst == NULL)
return -1;
pipi_free(ctx->images[ctx->nimages - 2]);
pipi_free(ctx->images[ctx->nimages - 1]);
ctx->images[ctx->nimages - 2] = dst;
ctx->nimages--;
}
else if(!strcmp(cmd, "wrap"))
{
if(ctx->nimages < 1)
return -1;
ctx->images[ctx->nimages - 1]->wrap = 1;
}
else if(!strcmp(cmd, "autocontrast"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_autocontrast(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "invert"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_invert(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "dilate"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_dilate(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "erode"))
{
pipi_image_t *tmp;
if(ctx->nimages < 1)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = pipi_erode(tmp);
pipi_free(tmp);
}
else if(!strcmp(cmd, "gray"))
{
if(ctx->nimages < 1)
return -1;
pipi_get_pixels(ctx->images[ctx->nimages - 1], PIPI_PIXELS_Y_F32);
}
else if(!strcmp(cmd, "free"))
{
if(ctx->nimages < 1)
return -1;
ctx->nimages--;
pipi_free(ctx->images[ctx->nimages]);
}
else if(!strcmp(cmd, "dup"))
{
if(ctx->nimages < 1)
return -1;
ctx->images[ctx->nimages] = pipi_copy(ctx->images[ctx->nimages - 1]);
ctx->nimages++;
}
else if(!strcmp(cmd, "swap"))
{
pipi_image_t *tmp;
if(ctx->nimages < 2)
return -1;
tmp = ctx->images[ctx->nimages - 1];
ctx->images[ctx->nimages - 1] = ctx->images[ctx->nimages - 2];
ctx->images[ctx->nimages - 2] = tmp;
}
else if(!strcmp(cmd, "roll"))
{
pipi_image_t *tmp;
char const *arg;
va_list ap;
int val;

va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);
val = atoi(arg);
if(val <= 0 || ctx->nimages < val)
return -1;
if(val == 1)
return 0;
tmp = ctx->images[ctx->nimages - val];
memmove(ctx->images + ctx->nimages - val,
ctx->images + ctx->nimages - val + 1,
(val - 1) * sizeof(*ctx->images));
ctx->images[ctx->nimages - 1] = tmp;
}
else if(!strcmp(cmd, "line"))
{
char const *arg;
va_list ap;
int x1, y1, x2, y2, aa = 0, ret;
uint32_t color = 0;

if(ctx->nimages < 1)
return -1;

va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);

ret = sscanf(arg, "%d,%d,%d,%d,%08x,%d",
&x1, &y1, &x2, &y2, &color, &aa);
if(ret < 5) return -1;

ctx->images[ctx->nimages] = pipi_copy(ctx->images[ctx->nimages - 1]);
pipi_draw_line(ctx->images[ctx->nimages],
x1, y1, x2, y2, color, aa);
ctx->nimages++;
}
else if(!strcmp(cmd, "sine") || !strcmp(cmd, "wave"))
{
pipi_image_t *src, *dst = NULL;
char const *arg;
va_list ap;
float dw, dh, d = 0.0, a = 0.0;
int ret;

if(ctx->nimages < 1)
return -1;

va_start(ap, cmd);
arg = va_arg(ap, char const *);
va_end(ap);

ret = sscanf(arg, "%gx%g+%gr%g", &dw, &dh, &d, &a);
if(ret < 2)
return -1;

src = ctx->images[ctx->nimages - 1];
switch(cmd[0])
{
case 's': dst = pipi_sine(src, dw, dh, d, a); break;
case 'w': dst = pipi_wave(src, dw, dh, d, a); break;
}
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else if(!strcmp(cmd, "rgb2yuv") || !strcmp(cmd, "yuv2rgb"))
{
pipi_image_t *src, *dst = NULL;
if(ctx->nimages < 1)
return -1;
src = ctx->images[ctx->nimages - 1];
switch (cmd[0])
{
case 'r': dst = pipi_rgb2yuv(src); break;
case 'y': dst = pipi_yuv2rgb(src); break;
}
if(dst == NULL)
return -1;
pipi_free(src);
ctx->images[ctx->nimages - 1] = dst;
}
else
{
return -1;
}

return 0;
}


+ 68
- 0
src/image/crop.cpp Voir le fichier

@@ -0,0 +1,68 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2009 Sam Hocevar <sam@hocevar.net>
* 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.
*/

/*
* crop.c: image cropping functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_crop(pipi_image_t *src, int w, int h, int dx, int dy)
{
float *srcdata, *dstdata;
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
int y, off, len;

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

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

off = dx;
len = w;

if(dx < 0)
{
len += dx;
dx = 0;
}

if(dx + len > srcp->w)
len = srcp->w - dx;

if(len > 0)
{
for(y = 0; y < h; y++)
{
if(y + dy < 0 || y + dy >= srcp->h)
continue;

memcpy(dstdata + y * w * 4,
srcdata + ((y + dy) * srcp->w + dx) * 4,
len * 4 * sizeof(float));
}
}

return dst;
}


+ 93
- 0
src/image/dither.cpp Voir le fichier

@@ -0,0 +1,93 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* dither.c: dithering functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

/* FIXME: this is not the right place for this... see pixels.c instead */
void pipi_dither_24to16(pipi_image_t *img)
{
int *error, *nexterror;
pipi_pixels_t *p;
uint32_t *p32;
int x, y;

error = malloc(sizeof(int) * 3 * (img->w + 2));
nexterror = malloc(sizeof(int) * 3 * (img->w + 2));
p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8);
p32 = (uint32_t *)p->pixels;

memset(error, 0, sizeof(int) * 3 * (img->w + 2));

for(y = 0; y < img->h; y++)
{
int er = 0, eg = 0, eb = 0;

memset(nexterror, 0, sizeof(int) * 3 * (img->w + 2));

for(x = 0; x < img->w; x++)
{
int r, g, b, a, r2, g2, b2;
r = p32[y * img->w + x] & 0xff;
g = (p32[y * img->w + x] >> 8) & 0xff;
b = (p32[y * img->w + x] >> 16) & 0xff;
a = (p32[y * img->w + x] >> 24) & 0xff;
r += er + error[x * 3 + 3];
g += eg + error[x * 3 + 4];
b += eb + error[x * 3 + 5];
r2 = r / 8 * 8; g2 = g / 4 * 4; b2 = b / 8 * 8;
if(r2 < 0) r2 = 0; if(r2 > 0xf8) r2 = 0xf8;
if(g2 < 0) g2 = 0; if(g2 > 0xfc) g2 = 0xfc;
if(b2 < 0) b2 = 0; if(b2 > 0xf8) b2 = 0xf8;
/* hack */
if(r2 == 0x88 && g2 == 0x88 && b2 == 0x88) g2 = 0x84;
/* hack */
p32[y * img->w + x] = (a << 24) | (b2 << 16) | (g2 << 8) | r2;

er = r - (r2 / 8 * 255 / 31);
eg = g - (g2 / 4 * 255 / 63);
eb = b - (b2 / 8 * 255 / 31);
nexterror[x * 3 + 0] += er * 3 / 8;
nexterror[x * 3 + 1] += eg * 3 / 8;
nexterror[x * 3 + 2] += eb * 3 / 8;
nexterror[x * 3 + 3] += er * 5 / 8;
nexterror[x * 3 + 4] += eg * 5 / 8;
nexterror[x * 3 + 5] += eb * 5 / 8;
nexterror[x * 3 + 6] += er * 1 / 8;
nexterror[x * 3 + 7] += eg * 1 / 8;
nexterror[x * 3 + 8] += eb * 1 / 8;
er -= er * 3 / 8 + er * 7 / 8 + er * 1 / 8;
eg -= eg * 3 / 8 + eg * 7 / 8 + eg * 1 / 8;
eb -= eb * 3 / 8 + eb * 7 / 8 + eb * 1 / 8;
}

memcpy(error, nexterror, sizeof(int) * 3 * (img->w + 2));
}

pipi_release_pixels(img, p);

free(error);
free(nexterror);
}


+ 228
- 0
src/image/dither/dbs.cpp Voir le fichier

@@ -0,0 +1,228 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* dbs.c: Direct Binary Search dithering functions
*/

#include "config.h"

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

#include <math.h>

#include "pipi.h"
#include "pipi-internals.h"

#define CELL 16

#define N 7
#define NN ((N * 2 + 1))

/* FIXME: though the algorithm is supposed to stop, we do not have a real,
* guaranteed stop condition here. */

pipi_image_t *pipi_dither_dbs(pipi_image_t *img)
{
double kernel[NN * NN];
double t = 0.;
pipi_image_t *src, *dst, *tmp1, *tmp2;
pipi_pixels_t *dstp, *tmp1p, *tmp2p;
int *changelist;
float *dstdata, *tmp1data, *tmp2data;
int i, j, x, y, w, h, cw, ch;

/* Build our human visual system kernel. */
for(j = 0; j < NN; j++)
for(i = 0; i < NN; i++)
{
double a = (double)(i - N);
double b = (double)(j - N);
kernel[j * NN + i] =
exp(-(a * a + b * b) / (2. * 1.6 * 1.6))
+ exp(-(a * a + b * b) / (2. * 0.6 * 0.6));
t += kernel[j * NN + i];
}

for(j = 0; j < NN; j++)
for(i = 0; i < NN; i++)
kernel[j * NN + i] /= t;

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

cw = (w + CELL - 1) / CELL;
ch = (h + CELL - 1) / CELL;
changelist = malloc(cw * ch * sizeof(int));
memset(changelist, 0, cw * ch * sizeof(int));

src = pipi_copy(img);
pipi_get_pixels(src, PIPI_PIXELS_Y_F32);

tmp1 = pipi_convolution(src, NN, NN, kernel);
tmp1p = pipi_get_pixels(tmp1, PIPI_PIXELS_Y_F32);
tmp1data = (float *)tmp1p->pixels;

dst = pipi_dither_random(src);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_Y_F32);
dstdata = (float *)dstp->pixels;

pipi_free(src);

tmp2 = pipi_convolution(dst, NN, NN, kernel);
tmp2p = pipi_get_pixels(tmp2, PIPI_PIXELS_Y_F32);
tmp2data = (float *)tmp2p->pixels;

for(;;)
{
int cx, cy, n;
int allchanges = 0;

for(cy = 0; cy < ch; cy++)
for(cx = 0; cx < cw; cx++)
{
int changes = 0;

if(changelist[cy * cw + cx] >= 2)
continue;

for(y = cy * CELL; y < (cy + 1) * CELL; y++)
for(x = cx * CELL; x < (cx + 1) * CELL; x++)
{
double d, d2, e, best = 0.;
int opx = -1, opy = -1;

d = dstdata[y * w + x];

/* Compute the effect of a toggle */
e = 0.;
for(j = -N; j < N + 1; j++)
{
if(y + j < 0 || y + j >= h)
continue;

for(i = -N; i < N + 1; i++)
{
double m, p, q1, q2;

if(x + i < 0 || x + i >= w)
continue;

m = kernel[(j + N) * NN + i + N];
p = tmp1data[(y + j) * w + x + i];
q1 = tmp2data[(y + j) * w + x + i];
q2 = q1 - m * d + m * (1. - d);
e += (q1 - p) * (q1 - p) - (q2 - p) * (q2 - p);
}
}
if(e > best)
{
best = e;
opx = opy = 0;
}

/* Compute the effect of swaps */
for(n = 0; n < 8; n++)
{
static int const step[] =
{ 0, 1, 0, -1, -1, 0, 1, 0, -1, -1, -1, 1, 1, -1, 1, 1 };
int idx = step[n * 2], idy = step[n * 2 + 1];
if(y + idy < 0 || y + idy >= h
|| x + idx < 0 || x + idx >= w)
continue;
d2 = dstdata[(y + idy) * w + x + idx];
if(d2 == d)
continue;
e = 0.;
for(j = -N; j < N + 1; j++)
{
if(y + j < 0 || y + j >= h)
continue;
if(j - idy + N < 0 || j - idy + N >= NN)
continue;
for(i = -N; i < N + 1; i++)
{
double ma, mb, p, q1, q2;
if(x + i < 0 || x + i >= w)
continue;
if(i - idx + N < 0 || i - idx + N >= NN)
continue;
ma = kernel[(j + N) * NN + i + N];
mb = kernel[(j - idy + N) * NN + i - idx + N];
p = tmp1data[(y + j) * w + x + i];
q1 = tmp2data[(y + j) * w + x + i];
q2 = q1 - ma * d + ma * d2 - mb * d2 + mb * d;
e += (q1 - p) * (q1 - p) - (q2 - p) * (q2 - p);
}
}
if(e > best)
{
best = e;
opx = idx;
opy = idy;
}
}

/* Apply the change if interesting */
if(best <= 0.)
continue;
if(opx || opy)
{
d2 = dstdata[(y + opy) * w + x + opx];
dstdata[(y + opy) * w + x + opx] = d;
}
else
d2 = 1. - d;
dstdata[y * w + x] = d2;
for(j = -N; j < N + 1; j++)
for(i = -N; i < N + 1; i++)
{
double m = kernel[(j + N) * NN + i + N];
if(y + j >= 0 && y + j < h
&& x + i >= 0 && x + i < w)
{
t = tmp2data[(y + j) * w + x + i];
tmp2data[(y + j) * w + x + i] = t + m * (d2 - d);
}
if((opx || opy) && y + opy + j >= 0 && y + opy + j < h
&& x + opx + i >= 0 && x + opx + i < w)
{
t = tmp2data[(y + opy + j) * w + x + opx + i];
tmp2data[(y + opy + j) * w + x + opx + i]
= t + m * (d - d2);
}
}

changes++;
}

if(changes == 0)
changelist[cy * cw + cx]++;

allchanges += changes;
}

if(allchanges == 0)
break;
}

free(changelist);

pipi_free(tmp1);
pipi_free(tmp2);

return dst;
}


+ 86
- 0
src/image/dither/ediff.cpp Voir le fichier

@@ -0,0 +1,86 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* ed.c: generic error diffusion functions
*/

#include "config.h"

#include "pipi.h"
#include "pipi-internals.h"

/* Perform a generic error diffusion dithering. The first non-zero
* element in ker is treated as the current pixel. All other non-zero
* elements are the error diffusion coefficients.
* Making the matrix generic is not terribly slower: the performance
* hit is around 4% for Floyd-Steinberg and 13% for JaJuNi, with the
* benefit of a lot less code. */
pipi_image_t *pipi_dither_ediff(pipi_image_t *img, pipi_image_t *ker,
pipi_scan_t scan)
{
pipi_image_t *dst;
pipi_pixels_t *dstp, *kerp;
float *dstdata, *kerdata;
int x, y, w, h, i, j, kx, kw, kh;

w = img->w;
h = img->h;
kw = ker->w;
kh = ker->h;

dst = pipi_copy(img);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_Y_F32);
dstdata = (float *)dstp->pixels;

kerp = pipi_get_pixels(ker, PIPI_PIXELS_Y_F32);
kerdata = (float *)kerp->pixels;
for(kx = 0; kx < kw; kx++)
if(kerdata[kx] > 0)
break;

for(y = 0; y < h; y++)
{
int reverse = (y & 1) && (scan == PIPI_SCAN_SERPENTINE);

for(x = 0; x < w; x++)
{
float p, q, e;
int x2 = reverse ? w - 1 - x : x;
int s = reverse ? -1 : 1;

p = dstdata[y * w + x2];
q = p < 0.5 ? 0. : 1.;
dstdata[y * w + x2] = q;

e = (p - q);

for(j = 0; j < kh && y < h - j; j++)
for(i = 0; i < kw; i++)
{
if(j == 0 && i <= kx)
continue;

if(x + i - kx < 0 || x + i - kx >= w)
continue;

dstdata[(y + j) * w + x2 + (i - kx) * s]
+= e * kerdata[j * kw + i];
}
}
}

return dst;
}


+ 144
- 0
src/image/dither/ordered.cpp Voir le fichier

@@ -0,0 +1,144 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* ordered.c: Bayer ordered dithering functions
*/

#include "config.h"

#include <stdlib.h>
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_dither_halftone(pipi_image_t *img, double r, double angle)
{
#define PRECISION 4.
pipi_image_t *ret, *kernel;
int k = (r * PRECISION / 2. / sqrt(2.) + .5);

kernel = pipi_render_halftone(k, k);
ret = pipi_dither_ordered_ext(img, kernel, 1. / PRECISION, angle + 45.);
pipi_free(kernel);

return ret;
}

pipi_image_t *pipi_dither_ordered(pipi_image_t *img, pipi_image_t *kernel)
{
return pipi_dither_ordered_ext(img, kernel, 1.0, 0.0);
}

pipi_image_t *pipi_dither_ordered_ext(pipi_image_t *img, pipi_image_t *kernel,
double scale, double angle)
{
double sint, cost;
pipi_image_t *dst;
pipi_pixels_t *dstp, *kernelp;
float *dstdata, *kerneldata;
int x, y, w, h, kx, ky, kw, kh;

w = img->w;
h = img->h;
kw = kernel->w;
kh = kernel->h;

cost = cos(angle * (M_PI / 180));
sint = sin(angle * (M_PI / 180));

dst = pipi_copy(img);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_Y_F32);
dstdata = (float *)dstp->pixels;

kernelp = pipi_get_pixels(kernel, PIPI_PIXELS_Y_F32);
kerneldata = (float *)kernelp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
float p, q;

kx = (int)((cost * x - sint * y + 2 * w * h) / scale) % kw;
ky = (int)((cost * y + sint * x + 2 * w * h) / scale) % kh;

p = dstdata[y * w + x];
q = p > kerneldata[ky * kw + kx] ? 1. : 0.;
dstdata[y * w + x] = q;
}
}

return dst;
}

typedef struct
{
int x, y;
double val;
}
dot_t;

static int cmpdot(const void *p1, const void *p2)
{
return ((dot_t const *)p1)->val > ((dot_t const *)p2)->val;
}

pipi_image_t *pipi_order(pipi_image_t *src)
{
double epsilon;
pipi_image_t *dst;
pipi_pixels_t *dstp, *srcp;
float *dstdata, *srcdata;
dot_t *circle;
int x, y, w, h, n;

w = src->w;
h = src->h;
epsilon = 1. / (w * h + 1);

srcp = pipi_get_pixels(src, PIPI_PIXELS_Y_F32);
srcdata = (float *)srcp->pixels;

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

circle = malloc(w * h * sizeof(dot_t));

for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
{
circle[y * w + x].x = x;
circle[y * w + x].y = y;
circle[y * w + x].val = srcdata[y * w + x];
}
qsort(circle, w * h, sizeof(dot_t), cmpdot);

for(n = 0; n < w * h; n++)
{
x = circle[n].x;
y = circle[n].y;
dstdata[y * w + x] = (float)(n + 1) * epsilon;
}

free(circle);

return dst;
}


+ 117
- 0
src/image/dither/ostromoukhov.cpp Voir le fichier

@@ -0,0 +1,117 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* ostromoukhov.c: Ostromoukhov dithering functions
*
* This module implements Ostromoukhov's simple error diffusion algorithm,
* as introduced in the paper "A Simple and Efficient Error-Diffusion
* Algorithm", Proceedings of SIGGRAPH 2001, in ACM Computer Graphics,
* Annual Conference Series, pp. 567--572, 2001.
*
* TODO: the table is actually a piecewise linear function, so it should
* be easy to change it into something that works perfectly with floats.
*/

#include "config.h"

#include "pipi.h"
#include "pipi-internals.h"

static int const table[][3] =
{
{13, 0, 5}, {13, 0, 5}, {21, 0, 10}, {7, 0, 4},
{8, 0, 5}, {47, 3, 28}, {23, 3, 13}, {15, 3, 8},
{22, 6, 11}, {43, 15, 20}, {7, 3, 3}, {501, 224, 211},
{249, 116, 103}, {165, 80, 67}, {123, 62, 49}, {489, 256, 191},
{81, 44, 31}, {483, 272, 181}, {60, 35, 22}, {53, 32, 19},
{237, 148, 83}, {471, 304, 161}, {3, 2, 1}, {481, 314, 185},
{354, 226, 155}, {1389, 866, 685}, {227, 138, 125}, {267, 158, 163},
{327, 188, 220}, {61, 34, 45}, {627, 338, 505}, {1227, 638, 1075},
{20, 10, 19}, {1937, 1000, 1767}, {977, 520, 855}, {657, 360, 551},
{71, 40, 57}, {2005, 1160, 1539}, {337, 200, 247}, {2039, 1240, 1425},
{257, 160, 171}, {691, 440, 437}, {1045, 680, 627}, {301, 200, 171},
{177, 120, 95}, {2141, 1480, 1083}, {1079, 760, 513}, {725, 520, 323},
{137, 100, 57}, {2209, 1640, 855}, {53, 40, 19}, {2243, 1720, 741},
{565, 440, 171}, {759, 600, 209}, {1147, 920, 285}, {2311, 1880, 513},
{97, 80, 19}, {335, 280, 57}, {1181, 1000, 171}, {793, 680, 95},
{599, 520, 57}, {2413, 2120, 171}, {405, 360, 19}, {2447, 2200, 57},
{11, 10, 0}, {158, 151, 3}, {178, 179, 7}, {1030, 1091, 63},
{248, 277, 21}, {318, 375, 35}, {458, 571, 63}, {878, 1159, 147},
{5, 7, 1}, {172, 181, 37}, {97, 76, 22}, {72, 41, 17},
{119, 47, 29}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1},
{4, 1, 1}, {4, 1, 1}, {4, 1, 1}, {4, 1, 1},
{4, 1, 1}, {4, 1, 1}, {65, 18, 17}, {95, 29, 26},
{185, 62, 53}, {30, 11, 9}, {35, 14, 11}, {85, 37, 28},
{55, 26, 19}, {80, 41, 29}, {155, 86, 59}, {5, 3, 2},
{5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2},
{5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2},
{5, 3, 2}, {5, 3, 2}, {5, 3, 2}, {5, 3, 2},
{305, 176, 119}, {155, 86, 59}, {105, 56, 39}, {80, 41, 29},
{65, 32, 23}, {55, 26, 19}, {335, 152, 113}, {85, 37, 28},
{115, 48, 37}, {35, 14, 11}, {355, 136, 109}, {30, 11, 9},
{365, 128, 107}, {185, 62, 53}, {25, 8, 7}, {95, 29, 26},
{385, 112, 103}, {65, 18, 17}, {395, 104, 101}, {4, 1, 1}
};

pipi_image_t *pipi_dither_ostromoukhov(pipi_image_t *img, pipi_scan_t scan)
{
pipi_image_t *dst;
pipi_pixels_t *dstp;
float *dstdata;
int x, y, w, h;

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

dst = pipi_copy(img);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_Y_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
int reverse = (y & 1) && (scan == PIPI_SCAN_SERPENTINE);

for(x = 0; x < w; x++)
{
float p, q, e;
int x2, s, i;

x2 = reverse ? w - 1 - x : x;
s = reverse ? -1 : 1;

p = dstdata[y * w + x2];
q = p < 0.5 ? 0. : 1.;
dstdata[y * w + x2] = q;

e = p - q;
i = p * 255.9999;
if(i > 127) i = 255 - i;
if(i < 0) i = 0; /* XXX: no "else" here */
e /= table[i][0] + table[i][1] + table[i][2];

if(x < w - 1)
dstdata[y * w + x2 + s] += e * table[i][0];
if(y < h - 1)
{
if(x > 0)
dstdata[(y + 1) * w + x2 - s] += e * table[i][1];
dstdata[(y + 1) * w + x2] += e * table[i][2];
}
}
}

return dst;
}


+ 60
- 0
src/image/dither/random.cpp Voir le fichier

@@ -0,0 +1,60 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* random.c: random dithering functions
*/

#include "config.h"

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_dither_random(pipi_image_t *img)
{
pipi_image_t *dst;
pipi_pixels_t *dstp;
float *dstdata;
unsigned int ctx = 1;
int x, y, w, h;

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

dst = pipi_copy(img);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_Y_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
long hi, lo;
float p, q;

hi = ctx / 12773L;
lo = ctx % 12773L;
ctx = 16807L * lo - 2836L * hi;
if(ctx <= 0)
ctx += 0x7fffffffL;

p = dstdata[y * w + x];
q = p > (double)((ctx % 65536) / 65535.) ? 1. : 0.;
dstdata[y * w + x] = q;
}
}

return dst;
}


+ 111
- 0
src/image/filter/autocontrast.cpp Voir le fichier

@@ -0,0 +1,111 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* autocontrast.c: autocontrast functions
* TODO: the current approach is naive; we should use the histogram in order
* to decide how to change the contrast.
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_autocontrast(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
float min = 1.0, max = 0.0, t;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
if(srcdata[y * w + x] < min)
min = srcdata[y * w + x];
if(srcdata[y * w + x] > max)
max = srcdata[y * w + x];
}
else
{
if(srcdata[4 * (y * w + x)] < min)
min = srcdata[4 * (y * w + x)];
if(srcdata[4 * (y * w + x)] > max)
max = srcdata[4 * (y * w + x)];
if(srcdata[4 * (y * w + x) + 1] < min)
min = srcdata[4 * (y * w + x) + 1];
if(srcdata[4 * (y * w + x) + 1] > max)
max = srcdata[4 * (y * w + x) + 1];
if(srcdata[4 * (y * w + x) + 2] < min)
min = srcdata[4 * (y * w + x) + 2];
if(srcdata[4 * (y * w + x) + 2] > max)
max = srcdata[4 * (y * w + x) + 2];
}
}
}

if(min >= max)
return pipi_copy(src);

t = 1. / (max - min);

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
dstdata[y * w + x] = (srcdata[y * w + x] - min) * t;
}
else
{
dstdata[4 * (y * w + x)]
= (srcdata[4 * (y * w + x)] - min) * t;
dstdata[4 * (y * w + x) + 1]
= (srcdata[4 * (y * w + x) + 1] - min) * t;
dstdata[4 * (y * w + x) + 2]
= (srcdata[4 * (y * w + x) + 2] - min) * t;
dstdata[4 * (y * w + x) + 3]
= srcdata[4 * (y * w + x) + 3];
}
}
}

return dst;
}


+ 315
- 0
src/image/filter/blur.cpp Voir le fichier

@@ -0,0 +1,315 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* blur.c: blur functions
*/

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

#if !defined TEMPLATE_FILE /* This file uses the template system */
#define TEMPLATE_FLAGS SET_FLAG_GRAY | SET_FLAG_WRAP
#define TEMPLATE_FILE "filter/blur.c"
#include "pipi-template.h"

/* Any standard deviation below this value will be rounded up, in order
* to avoid ridiculously low values. exp(-1/(2*0.2*0.2)) is < 10^-5 so
* there is little chance that any value below 0.2 will be useful. */
#define BLUR_EPSILON 0.2

pipi_image_t *pipi_gaussian_blur(pipi_image_t *src, float radius)
{
return pipi_gaussian_blur_ext(src, radius, radius, 0.0, 0.0, 0.0);
}

pipi_image_t *pipi_gaussian_blur_ext(pipi_image_t *src, float rx, float ry,
float angle, float dx, float dy)
{
pipi_image_t *ret;
double *kernel;
double Kx, Ky, t = 0.0, sint, cost, bbx, bby;
int i, j, krx, kry, m, n;

if(rx < BLUR_EPSILON) rx = BLUR_EPSILON;
if(ry < BLUR_EPSILON) ry = BLUR_EPSILON;

sint = sin(angle * M_PI / 180.);
cost = cos(angle * M_PI / 180.);

/* Compute the final ellipse's bounding box */
bbx = sqrt(rx * rx * cost * cost + ry * ry * sint * sint);
bby = sqrt(ry * ry * cost * cost + rx * rx * sint * sint);

/* FIXME: the kernel becomes far too big with large values of dx, because
* we grow both left and right. Fix the growing direction. */
krx = (int)(3. * bbx + .99999 + ceil(abs(dx)));
m = 2 * krx + 1;
Kx = -1. / (2. * rx * rx);

kry = (int)(3. * bby + .99999 + ceil(abs(dy)));
n = 2 * kry + 1;
Ky = -1. / (2. * ry * ry);

kernel = malloc(m * n * sizeof(double));

for(j = -kry; j <= kry; j++)
{
for(i = -krx; i <= krx; i++)
{
/* FIXME: this level of interpolation sucks. We should
* interpolate on the full NxN grid for better quality. */
static double const samples[] =
{
.0, .0, 1,
-.40, -.40, 0.8,
-.30, .0, 0.9,
-.40, .40, 0.8,
.0, .30, 0.9,
.40, .40, 0.8,
.30, .0, 0.9,
.40, -.40, 0.8,
.0, -.30, 0.9,
};
double u, v, ex, ey, d = 0.;
unsigned int k;

for(k = 0; k < sizeof(samples) / sizeof(*samples) / 3; k++)
{
u = ((double)i + samples[k * 3]) * cost
- ((double)j + samples[k * 3 + 1]) * sint + dx;
v = ((double)i + samples[k * 3]) * sint
+ ((double)j + samples[k * 3 + 1]) * cost + dy;
ex = Kx * u * u;
ey = Ky * v * v;
d += samples[k * 3 + 2] * exp(ex + ey);

/* Do not interpolate if this is a standard gaussian. */
if(!dx && !dy && !angle)
break;
}

kernel[(j + kry) * m + i + krx] = d;
t += d;
}
}

for(j = 0; j < n; j++)
for(i = 0; i < m; i++)
kernel[j * m + i] /= t;

ret = pipi_convolution(src, m, n, kernel);

free(kernel);

return ret;
}

pipi_image_t *pipi_box_blur(pipi_image_t *src, int size)
{
return pipi_box_blur_ext(src, size, size);
}

pipi_image_t *pipi_box_blur_ext(pipi_image_t *src, int m, int n)
{
if(src->wrap)
{
if(src->last_modified == PIPI_PIXELS_Y_F32)
return boxblur_gray_wrap(src, m, n);

return boxblur_wrap(src, m, n);
}
else
{
if(src->last_modified == PIPI_PIXELS_Y_F32)
return boxblur_gray(src, m, n);

return boxblur(src, m, n);
}
}

#else /* XXX: the following functions use the template system */

static pipi_image_t *T(boxblur)(pipi_image_t *src, int m, int n)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double *acc;
int x, y, w, h, i, j, i2, j2, size;

w = src->w;
h = src->h;
size = (2 * m + 1) * (2 * n + 1);

srcp = FLAG_GRAY ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = FLAG_GRAY ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

acc = malloc(w * (FLAG_GRAY ? 1 : 4) * sizeof(double));

/* Step 1: fill the accumulator */
for(x = 0; x < w; x++)
{
double r = 0., g = 0., b = 0., a = 0.;
double t = 0.;

for(j = -n; j <= n; j++)
{
if(FLAG_WRAP)
j2 = (j < 0) ? h - 1 - ((-j - 1) % h) : j % h;
else
j2 = (j < 0) ? 0 : (j >= h) ? h - 1 : j;

if(FLAG_GRAY)
t += srcdata[j2 * w + x];
else
{
r += srcdata[4 * (j2 * w + x)];
g += srcdata[4 * (j2 * w + x) + 1];
b += srcdata[4 * (j2 * w + x) + 2];
a += srcdata[4 * (j2 * w + x) + 3];
}
}

if(FLAG_GRAY)
acc[x] = t;
else
{
acc[4 * x] = r;
acc[4 * x + 1] = g;
acc[4 * x + 2] = b;
acc[4 * x + 3] = a;
}
}

/* Step 2: blur the image, line by line */
for(y = 0; y < h; y++)
{
double r = 0., g = 0., b = 0., a = 0.;
double t = 0.;

/* 2.1: compute the first pixel */
for(i = -m; i <= m; i++)
{
if(FLAG_WRAP)
i2 = (i < 0) ? w - 1 - ((-i - 1) % w) : i % w;
else
i2 = (i < 0) ? 0 : (i >= w) ? w - 1 : i;

if(FLAG_GRAY)
t += acc[i2];
else
{
r += acc[4 * i2];
g += acc[4 * i2 + 1];
b += acc[4 * i2 + 2];
a += acc[4 * i2 + 3];
}
}

/* 2.2: iterate on the whole line */
for(x = 0; x < w; x++)
{
int u, u2, v, v2;

if(FLAG_GRAY)
{
dstdata[y * w + x] = t / size;
}
else
{
dstdata[4 * (y * w + x)] = r / size;
dstdata[4 * (y * w + x) + 1] = g / size;
dstdata[4 * (y * w + x) + 2] = b / size;
dstdata[4 * (y * w + x) + 3] = a / size;
}

u = x - m;
if(FLAG_WRAP)
u2 = (u < 0) ? w - 1 - ((-u - 1) % w) : u % w;
else
u2 = (u < 0) ? 0 : (u >= w) ? w - 1 : u;
v = x + m + 1;
if(FLAG_WRAP)
v2 = (v < 0) ? w - 1 - ((-v - 1) % w) : v % w;
else
v2 = (v < 0) ? 0 : (v >= w) ? w - 1 : v;
if(FLAG_GRAY)
{
t = t - acc[u2] + acc[v2];
}
else
{
r = r - acc[4 * u2] + acc[4 * v2];
g = g - acc[4 * u2 + 1] + acc[4 * v2 + 1];
b = b - acc[4 * u2 + 2] + acc[4 * v2 + 2];
a = a - acc[4 * u2 + 3] + acc[4 * v2 + 3];
}
}

/* 2.3: update the accumulator */
for(x = 0; x < w; x++)
{
int u, u2, v, v2;

u = y - n;
if(FLAG_WRAP)
u2 = (u < 0) ? h - 1 - ((-u - 1) % h) : u % h;
else
u2 = (u < 0) ? 0 : (u >= h) ? h - 1 : u;
v = y + n + 1;
if(FLAG_WRAP)
v2 = (v < 0) ? h - 1 - ((-v - 1) % h) : v % h;
else
v2 = (v < 0) ? 0 : (v >= h) ? h - 1 : v;
if(FLAG_GRAY)
{
acc[x] += srcdata[v2 * w + x] - srcdata[u2 * w + x];
}
else
{
int uoff = 4 * (u2 * w + x);
int voff = 4 * (v2 * w + x);

acc[4 * x] += srcdata[voff] - srcdata[uoff];
acc[4 * x + 1] += srcdata[voff + 1] - srcdata[uoff + 1];
acc[4 * x + 2] += srcdata[voff + 2] - srcdata[uoff + 2];
acc[4 * x + 3] += srcdata[voff + 3] - srcdata[uoff + 3];
}
}
}

free(acc);

return dst;
}

#endif


+ 288
- 0
src/image/filter/color.cpp Voir le fichier

@@ -0,0 +1,288 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* color.c: colour manipulation functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_brightness(pipi_image_t *src, double val)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

if(val >= 0.0)
{
for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
double p = srcdata[y * w + x];
dstdata[y * w + x] = p < 1. - val ? p + val : 1.;
}
else
{
double p;
int d = 4 * (y * w + x);

p = srcdata[d];
dstdata[d] = p < 1. - val ? p + val : 1.;
p = srcdata[d + 1];
dstdata[d + 1] = p < 1. - val ? p + val : 1.;
p = srcdata[d + 2];
dstdata[d + 2] = p < 1. - val ? p + val : 1.;
p = srcdata[d + 3];
dstdata[d + 3] = p;
}
}
}
}
else
{
for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
double p = srcdata[y * w + x];
dstdata[y * w + x] = p > -val ? p + val : 0.;
}
else
{
double p;
int d = 4 * (y * w + x);

p = srcdata[d];
dstdata[d] = p > -val ? p + val : 0.;
p = srcdata[d + 1];
dstdata[d + 1] = p > -val ? p + val : 0.;
p = srcdata[d + 2];
dstdata[d + 2] = p > -val ? p + val : 0.;
p = srcdata[d + 3];
dstdata[d + 3] = p;
}
}
}
}

return dst;
}

pipi_image_t *pipi_contrast(pipi_image_t *src, double val)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

if(val >= 0.0)
{
if(val > 0.99999)
val = 0.99999;

val = 1. / (1. - val);

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
double p = (srcdata[y * w + x] - 0.5) * val + 0.5;
dstdata[y * w + x] = p < 0. ? 0. : p > 1. ? 1. : p;
}
else
{
double p;
int d = 4 * (y * w + x);

p = (srcdata[d] - 0.5) * val + 0.5;
dstdata[d] = p < 0. ? 0. : p > 1. ? 1. : p;
p = (srcdata[d + 1] - 0.5) * val + 0.5;
dstdata[d + 1] = p < 0. ? 0. : p > 1. ? 1. : p;
p = (srcdata[d + 2] - 0.5) * val + 0.5;
dstdata[d + 2] = p < 0. ? 0. : p > 1. ? 1. : p;
p = srcdata[d + 3];
dstdata[d + 3] = p;
}
}
}
}
else
{
if(val < -1.)
val = -1.;

val = 1. + val;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
double p = srcdata[y * w + x];
dstdata[y * w + x] = (p - 0.5) * val + 0.5;
}
else
{
double p;
int d = 4 * (y * w + x);

p = srcdata[d];
dstdata[d] = (p - 0.5) * val + 0.5;
p = srcdata[d + 1];
dstdata[d + 1] = (p - 0.5) * val + 0.5;
p = srcdata[d + 2];
dstdata[d + 2] = (p - 0.5) * val + 0.5;
p = srcdata[d + 3];
dstdata[d + 3] = p;
}
}
}
}

return dst;
}

pipi_image_t *pipi_invert(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
dstdata[y * w + x] = 1. - srcdata[y * w + x];
}
else
{
int d = 4 * (y * w + x);

dstdata[d] = 1. - srcdata[d];
dstdata[d + 1] = 1. - srcdata[d + 1];
dstdata[d + 2] = 1. - srcdata[d + 2];
dstdata[d + 3] = 1. - srcdata[d + 3];
}
}
}

return dst;
}

pipi_image_t *pipi_threshold(pipi_image_t *src, double val)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
dstdata[y * w + x] = srcdata[y * w + x] < val ? 0. : 1.;
}
else
{
int d = 4 * (y * w + x);

dstdata[d] = srcdata[d] < val ? 0. : 1.;
dstdata[d + 1] = srcdata[d + 1] < val ? 0. : 1.;
dstdata[d + 2] = srcdata[d + 2] < val ? 0. : 1.;
dstdata[d + 3] = srcdata[d + 3] < val ? 0. : 1.;
}
}
}

return dst;
}


+ 289
- 0
src/image/filter/convolution.cpp Voir le fichier

@@ -0,0 +1,289 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* convolution.c: generic convolution functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

#if !defined TEMPLATE_FILE /* This file uses the template system */
#define TEMPLATE_FLAGS SET_FLAG_GRAY | SET_FLAG_WRAP
#define TEMPLATE_FILE "filter/convolution.c"
#include "pipi-template.h"

pipi_image_t *pipi_convolution(pipi_image_t *src, int m, int n, double mat[])
{
pipi_image_t *ret;
double tmp;
double *hvec, *vvec;
int i, j, besti = -1, bestj = -1;

/* Find the cell with the largest value */
tmp = 0.0;
for(i = 0; i < m * n; i++)
if(mat[i] * mat[i] > tmp)
{
tmp = mat[i] * mat[i];
besti = i % m;
bestj = i / m;
}

/* If the kernel is empty, return an empty picture */
if(tmp == 0.0)
return pipi_new(src->w, src->h);

/* Check whether the matrix rank is 1 */
for(j = 0; j < n; j++)
{
if(j == bestj)
continue;

for(i = 0; i < m; i++)
{
double p, q;

if(i == besti)
continue;

p = mat[j * m + i] * mat[bestj * m + besti];
q = mat[bestj * m + i] * mat[j * m + besti];

if(fabs(p - q) > 0.0001 * 0.0001)
{
if(src->last_modified == PIPI_PIXELS_Y_F32)
{
if(src->wrap)
return conv_gray_wrap(src, m, n, mat);
return conv_gray(src, m, n, mat);
}
else
{
if(src->wrap)
return conv_wrap(src, m, n, mat);
return conv(src, m, n, mat);
}
}
}
}

/* Matrix rank is 1! Separate the filter */
hvec = malloc(m * sizeof(double));
vvec = malloc(n * sizeof(double));

tmp = sqrt(fabs(mat[bestj * m + besti]));
for(i = 0; i < m; i++)
hvec[i] = mat[bestj * m + i] / tmp;
for(j = 0; j < n; j++)
vvec[j] = mat[j * m + besti] / tmp;

if(src->last_modified == PIPI_PIXELS_Y_F32)
ret = src->wrap ? sepconv_gray_wrap(src, m, hvec, n, vvec)
: sepconv_gray(src, m, hvec, n, vvec);
else
ret = src->wrap ? sepconv_wrap(src, m, hvec, n, vvec)
: sepconv(src, m, hvec, n, vvec);

free(hvec);
free(vvec);

return ret;
}

#else /* XXX: the following functions use the template system */

static pipi_image_t *T(conv)(pipi_image_t *src, int m, int n, double mat[])
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, i, j, w, h;

w = src->w;
h = src->h;

srcp = FLAG_GRAY ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = FLAG_GRAY ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double R = 0., G = 0., B = 0., A = 0.;
double Y = 0.;
int x2, y2, off = 4 * (y * w + x);

for(j = 0; j < n; j++)
{
y2 = y + j - n / 2;
if(y2 < 0) y2 = FLAG_WRAP ? h - 1 - ((-y2 - 1) % h) : 0;
else if(y2 >= h) y2 = FLAG_WRAP ? y2 % h : h - 1;

for(i = 0; i < m; i++)
{
double f = mat[j * m + i];

x2 = x + i - m / 2;
if(x2 < 0) x2 = FLAG_WRAP ? w - 1 - ((-x2 - 1) % w) : 0;
else if(x2 >= w) x2 = FLAG_WRAP ? x2 % w : w - 1;

if(FLAG_GRAY)
Y += f * srcdata[y2 * w + x2];
else
{
R += f * srcdata[(y2 * w + x2) * 4];
G += f * srcdata[(y2 * w + x2) * 4 + 1];
B += f * srcdata[(y2 * w + x2) * 4 + 2];
A += f * srcdata[(y2 * w + x2) * 4 + 3];
}
}
}

if(FLAG_GRAY)
dstdata[y * w + x] = Y < 0.0 ? 0.0 : Y > 1.0 ? 1.0 : Y;
else
{
dstdata[off] = R < 0.0 ? 0.0 : R > 1.0 ? 1.0 : R;
dstdata[off + 1] = G < 0.0 ? 0.0 : G > 1.0 ? 1.0 : G;
dstdata[off + 2] = B < 0.0 ? 0.0 : B > 1.0 ? 1.0 : B;
dstdata[off + 3] = A < 0.0 ? 0.0 : A > 1.0 ? 1.0 : A;
}
}
}

return dst;
}

static pipi_image_t *T(sepconv)(pipi_image_t *src,
int m, double hvec[], int n, double vvec[])
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double *buffer;
int x, y, i, j, w, h;

w = src->w;
h = src->h;

srcp = FLAG_GRAY ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = FLAG_GRAY ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

buffer = malloc(w * h * (FLAG_GRAY ? 1 : 4) * sizeof(double));

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double R = 0., G = 0., B = 0., A = 0.;
double Y = 0.;
int x2, off = 4 * (y * w + x);

for(i = 0; i < m; i++)
{
double f = hvec[i];

x2 = x + i - m / 2;
if(x2 < 0) x2 = FLAG_WRAP ? w - 1 - ((-x2 - 1) % w) : 0;
else if(x2 >= w) x2 = FLAG_WRAP ? x2 % w : w - 1;

if(FLAG_GRAY)
Y += f * srcdata[y * w + x2];
else
{
R += f * srcdata[(y * w + x2) * 4];
G += f * srcdata[(y * w + x2) * 4 + 1];
B += f * srcdata[(y * w + x2) * 4 + 2];
A += f * srcdata[(y * w + x2) * 4 + 3];
}
}

if(FLAG_GRAY)
buffer[y * w + x] = Y;
else
{
buffer[off] = R;
buffer[off + 1] = G;
buffer[off + 2] = B;
buffer[off + 3] = A;
}
}
}

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double R = 0., G = 0., B = 0., A = 0.;
double Y = 0.;
int y2, off = 4 * (y * w + x);

for(j = 0; j < n; j++)
{
double f = vvec[j];

y2 = y + j - n / 2;
if(y2 < 0) y2 = FLAG_WRAP ? h - 1 - ((-y2 - 1) % h) : 0;
else if(y2 >= h) y2 = FLAG_WRAP ? y2 % h : h - 1;

if(FLAG_GRAY)
Y += f * buffer[y2 * w + x];
else
{
R += f * buffer[(y2 * w + x) * 4];
G += f * buffer[(y2 * w + x) * 4 + 1];
B += f * buffer[(y2 * w + x) * 4 + 2];
A += f * buffer[(y2 * w + x) * 4 + 3];
}
}

if(FLAG_GRAY)
dstdata[y * w + x] = Y < 0.0 ? 0.0 : Y > 1.0 ? 1.0 : Y;
else
{
dstdata[off] = R < 0.0 ? 0.0 : R > 1.0 ? 1.0 : R;
dstdata[off + 1] = G < 0.0 ? 0.0 : G > 1.0 ? 1.0 : G;
dstdata[off + 2] = B < 0.0 ? 0.0 : B > 1.0 ? 1.0 : B;
dstdata[off + 3] = A < 0.0 ? 0.0 : A > 1.0 ? 1.0 : A;
}
}
}

free(buffer);

return dst;
}

#endif


+ 170
- 0
src/image/filter/dilate.cpp Voir le fichier

@@ -0,0 +1,170 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* dilate.c: dilate and erode functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

/* FIXME: these functions are almost the same, try to merge them
* somewhat efficiently. */
/* TODO: - dilate by k (Manhattan distance)
* - dilate by r (euclidian distance, with non-integer r) */
pipi_image_t *pipi_dilate(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, i, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double t;
int x2, y2, x3, y3;

y2 = y - 1;
if(y2 < 0) y2 = h - 1;
y3 = y + 1;
if(y3 >= h) y3 = 0;

x2 = x - 1;
if(x2 < 0) x2 = w - 1;
x3 = x + 1;
if(x3 >= w) x3 = 0;

if(gray)
{
t = srcdata[y * w + x];
if(srcdata[y2 * w + x] > t) t = srcdata[y2 * w + x];
if(srcdata[y3 * w + x] > t) t = srcdata[y3 * w + x];
if(srcdata[y * w + x2] > t) t = srcdata[y * w + x2];
if(srcdata[y * w + x3] > t) t = srcdata[y * w + x3];
dstdata[y * w + x] = t;
}
else
{
for(i = 0; i < 4; i++)
{
t = srcdata[4 * (y * w + x) + i];
if(srcdata[4 * (y2 * w + x) + i] > t)
t = srcdata[4 * (y2 * w + x) + i];
if(srcdata[4 * (y3 * w + x) + i] > t)
t = srcdata[4 * (y3 * w + x) + i];
if(srcdata[4 * (y * w + x2) + i] > t)
t = srcdata[4 * (y * w + x2) + i];
if(srcdata[4 * (y * w + x3) + i] > t)
t = srcdata[4 * (y * w + x3) + i];
dstdata[4 * (y * w + x) + i] = t;
}
}
}
}

return dst;
}

pipi_image_t *pipi_erode(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, i, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double t;
int x2, y2, x3, y3;

y2 = y - 1;
if(y2 < 0) y2 = h - 1;
y3 = y + 1;
if(y3 >= h) y3 = 0;

x2 = x - 1;
if(x2 < 0) x2 = w - 1;
x3 = x + 1;
if(x3 >= w) x3 = 0;

if(gray)
{
t = srcdata[y * w + x];
if(srcdata[y2 * w + x] < t) t = srcdata[y2 * w + x];
if(srcdata[y3 * w + x] < t) t = srcdata[y3 * w + x];
if(srcdata[y * w + x2] < t) t = srcdata[y * w + x2];
if(srcdata[y * w + x3] < t) t = srcdata[y * w + x3];
dstdata[y * w + x] = t;
}
else
{
for(i = 0; i < 4; i++)
{
t = srcdata[4 * (y * w + x) + i];
if(srcdata[4 * (y2 * w + x) + i] < t)
t = srcdata[4 * (y2 * w + x) + i];
if(srcdata[4 * (y3 * w + x) + i] < t)
t = srcdata[4 * (y3 * w + x) + i];
if(srcdata[4 * (y * w + x2) + i] < t)
t = srcdata[4 * (y * w + x2) + i];
if(srcdata[4 * (y * w + x3) + i] < t)
t = srcdata[4 * (y * w + x3) + i];
dstdata[4 * (y * w + x) + i] = t;
}
}
}
}

return dst;
}


+ 134
- 0
src/image/filter/median.cpp Voir le fichier

@@ -0,0 +1,134 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* median.c: median filter functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

static int cmpdouble(void const *i1, void const *i2)
{
double a = *(double const *)i1;
double b = *(double const *)i2;

return (a > b) - (a < b);
}

pipi_image_t *pipi_median(pipi_image_t *src, int radius)
{
return pipi_median_ext(src, radius, radius);
}

/* FIXME: this is in desperate want of optimisation. Here is what could
* be done to improve the performance:
* - prefetch the neighbourhood; most neighbours are the same as the
* previous pixels.
* - use a better sort algorithm; bubble sort is ridiculous
* - even better, use state-of-the art median selection, ie. O(3n), for
* most common combinations (9, 25, 49, 81). */
pipi_image_t *pipi_median_ext(pipi_image_t *src, int rx, int ry)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double *list;
int x, y, w, h, i, j, size, gray;

w = src->w;
h = src->h;
size = (2 * rx + 1) * (2 * ry + 1);

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

list = malloc(size * (gray ? 1 : 4) * sizeof(double));

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double *parser = list;

/* Make a list of neighbours */
for(j = -ry; j <= ry; j++)
{
int y2 = y + j;
if(y2 < 0) y2 = h - 1 - ((-y2 - 1) % h);
else if(y2 > 0) y2 = y2 % h;

for(i = -rx; i <= rx; i++)
{
int x2 = x + i;
if(x2 < 0) x2 = w - 1 - ((-x2 - 1) % w);
else if(x2 > 0) x2 = x2 % w;

if(gray)
{
*parser++ = srcdata[y2 * w + x2];
}
else
{
parser[0] = srcdata[4 * (y2 * w + x2)];
parser[size * 1] = srcdata[4 * (y2 * w + x2) + 1];
parser[size * 2] = srcdata[4 * (y2 * w + x2) + 2];
parser[size * 3] = srcdata[4 * (y2 * w + x2) + 3];
parser++;
}
}
}

/* Sort the list */
qsort(list, size, sizeof(double), cmpdouble);
if(!gray)
{
qsort(list + size, size, sizeof(double), cmpdouble);
qsort(list + 2 * size, size, sizeof(double), cmpdouble);
qsort(list + 3 * size, size, sizeof(double), cmpdouble);
}

/* Store the median value */
if(gray)
{
dstdata[y * w + x] = list[size / 2];
}
else
{
dstdata[4 * (y * w + x)] = list[size / 2];
dstdata[4 * (y * w + x) + 1] = list[size / 2 + size * 1];
dstdata[4 * (y * w + x) + 2] = list[size / 2 + size * 2];
dstdata[4 * (y * w + x) + 3] = list[size / 2 + size * 3];
}
}
}

return dst;
}


+ 100
- 0
src/image/filter/rotate.cpp Voir le fichier

@@ -0,0 +1,100 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* wave.c: wave and other warping effects
*/

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_rotate(pipi_image_t *src, double a)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double sina, cosa, cx, cy;
int x, y, w, h, i, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

sina = sin(a * M_PI / 180.0);
cosa = cos(a * M_PI / 180.0);

cx = (double)w / 2.0;
cy = (double)h / 2.0;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double dx, dy;
int x2, y2;

dx = ((double)x - cx) * cosa - ((double)y - cy) * sina;
dy = ((double)y - cy) * cosa + ((double)x - cx) * sina;

x2 = (int)(cx + dx + 0.5);
y2 = (int)(cy + dy + 0.5);

if(gray)
{
if(x2 < 0 || y2 < 0 || x2 >= w || y2 >= h)
;
else
dstdata[y * w + x] = srcdata[y2 * w + x2];
}
else
{
if(x2 < 0 || y2 < 0 || x2 >= w || y2 >= h)
{
dstdata[4 * (y * w + x) + 3] = 0.0f;
}
else
{
for(i = 0; i < 4; i++)
{
dstdata[4 * (y * w + x) + i]
= srcdata[4 * (y2 * w + x2) + i];
}
}
}
}
}

return dst;
}


+ 128
- 0
src/image/filter/sharpen.cpp Voir le fichier

@@ -0,0 +1,128 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* sharpen.c: sharpen functions
*/

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_gaussian_sharpen(pipi_image_t *src, float radius)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double *kernel, *buffer;
double K, L;
int x, y, i, w, h, kr, kw;

w = src->w;
h = src->h;

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

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

kr = (int)(3. * radius + 0.99999);
kw = 2 * kr + 1;
K = 1. / (sqrt(2. * M_PI) * radius);
L = -1. / (2. * radius * radius);

kernel = malloc(kw * sizeof(double));
for(i = -kr; i <= kr; i++)
kernel[i + kr] = exp(L * i * i) * K;

buffer = malloc(w * h * 4 * sizeof(double));

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double R = 0., G = 0., B = 0., t = 0.;
int x2, off = 4 * (y * w + x);

for(i = -kr; i <= kr; i++)
{
double f = kernel[i + kr];

x2 = x + i;
if(x2 < 0) x2 = 0;
else if(x2 >= w) x2 = w - 1;

R += f * srcdata[(y * w + x2) * 4];
G += f * srcdata[(y * w + x2) * 4 + 1];
B += f * srcdata[(y * w + x2) * 4 + 2];
t += f;
}

buffer[off] = R / t;
buffer[off + 1] = G / t;
buffer[off + 2] = B / t;
}
}

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double R = 0., G = 0., B = 0., t = 0.;
int y2, off = 4 * (y * w + x);

for(i = -kr; i <= kr; i++)
{
double f = kernel[i + kr];

y2 = y + i;
if(y2 < 0) y2 = 0;
else if(y2 >= h) y2 = h - 1;

R += f * buffer[(y2 * w + x) * 4];
G += f * buffer[(y2 * w + x) * 4 + 1];
B += f * buffer[(y2 * w + x) * 4 + 2];
t += f;
}

dstdata[off] = 2 * srcdata[off] - R / t;
if(dstdata[off] < 0.0) dstdata[off] = 0.0;
if(dstdata[off] > 1.0) dstdata[off] = 1.0;
dstdata[off + 1] = 2 * srcdata[off + 1 ] - G / t;
if(dstdata[off + 1] < 0.0) dstdata[off + 1] = 0.0;
if(dstdata[off + 1] > 1.0) dstdata[off + 1] = 1.0;
dstdata[off + 2] = 2 * srcdata[off + 2 ] - B / t;
if(dstdata[off + 2] < 0.0) dstdata[off + 2] = 0.0;
if(dstdata[off + 2] > 1.0) dstdata[off + 2] = 1.0;
}
}

free(buffer);
free(kernel);

return dst;
}


+ 252
- 0
src/image/filter/transform.cpp Voir le fichier

@@ -0,0 +1,252 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* transform.c: basic transformation functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_hflip(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
dstdata[y * w + x] = srcdata[y * w + w - 1 - x];
}
else
{
dstdata[4 * (y * w + x)]
= srcdata[4 * (y * w + w - 1 - x)];
dstdata[4 * (y * w + x) + 1]
= srcdata[4 * (y * w + w - 1 - x) + 1];
dstdata[4 * (y * w + x) + 2]
= srcdata[4 * (y * w + w - 1 - x) + 2];
dstdata[4 * (y * w + x) + 3]
= srcdata[4 * (y * w + w - 1 - x) + 3];
}
}
}

return dst;
}

pipi_image_t *pipi_vflip(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
if(gray)
{
memcpy(dstdata + y * w,
srcdata + (h - 1 - y) * w,
w * sizeof(*dstdata));
}
else
{
memcpy(dstdata + 4 * y * w,
srcdata + 4 * (h - 1 - y) * w,
4 * w * sizeof(*dstdata));
}
}

return dst;
}

pipi_image_t *pipi_rotate90(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(h, w);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
dstdata[x * h + y] = srcdata[y * w + w - 1 - x];
}
else
{
dstdata[4 * (x * h + y)]
= srcdata[4 * (y * w + w - 1 - x)];
dstdata[4 * (x * h + y) + 1]
= srcdata[4 * (y * w + w - 1 - x) + 1];
dstdata[4 * (x * h + y) + 2]
= srcdata[4 * (y * w + w - 1 - x) + 2];
dstdata[4 * (x * h + y) + 3]
= srcdata[4 * (y * w + w - 1 - x) + 3];
}
}
}

return dst;
}

pipi_image_t *pipi_rotate180(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
dstdata[y * w + x] = srcdata[(h - 1 - y) * w + (w - 1 - x)];
}
else
{
dstdata[4 * (y * w + x)]
= srcdata[4 * ((h - 1 - y) * w + (w - 1 - x))];
dstdata[4 * (y * w + x) + 1]
= srcdata[4 * ((h - 1 - y) * w + (w - 1 - x)) + 1];
dstdata[4 * (y * w + x) + 2]
= srcdata[4 * ((h - 1 - y) * w + (w - 1 - x)) + 2];
dstdata[4 * (y * w + x) + 3]
= srcdata[4 * ((h - 1 - y) * w + (w - 1 - x)) + 3];
}
}
}

return dst;
}

pipi_image_t *pipi_rotate270(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(h, w);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
if(gray)
{
dstdata[x * h + h - 1 - y] = srcdata[y * w + x];
}
else
{
dstdata[4 * (x * h + h - 1 - y)]
= srcdata[4 * (y * w + x)];
dstdata[4 * (x * h + h - 1 - y) + 1]
= srcdata[4 * (y * w + x) + 1];
dstdata[4 * (x * h + h - 1 - y) + 2]
= srcdata[4 * (y * w + x) + 2];
dstdata[4 * (x * h + h - 1 - y) + 3]
= srcdata[4 * (y * w + x) + 3];
}
}
}

return dst;
}


+ 177
- 0
src/image/filter/wave.cpp Voir le fichier

@@ -0,0 +1,177 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* wave.c: wave and other warping effects
*/

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

#define BORDER 64

pipi_image_t *pipi_wave(pipi_image_t *src, double dw, double dh,
double d, double a)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double sina, cosa;
int x, y, w, h, i, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

sina = sin(a);
cosa = cos(a);

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double angle = 2 * M_PI / dw * ((x - w / 2) * cosa
+ (y - h / 2) * sina - d);
double displacement = dh * sin(angle);
double dx, dy;
int x2, y2;

dx = cosa * displacement;
dy = sina * displacement;

if(x < BORDER) dx = dx * x / BORDER;
if(x > w - 1 - BORDER) dx = dx * (w - 1 - x) / BORDER;
if(y < BORDER) dy = dy * y / BORDER;
if(y > h - 1 - BORDER) dy = dy * (h - 1 - y) / BORDER;

x2 = x + dx;
y2 = y + dy;

/* Just in case... */
if(x2 < 0) x2 = 0;
else if(x2 >= w) x2 = w - 1;

if(y2 < 0) y2 = 0;
else if(y2 >= h) y2 = h - 1;

if(gray)
{
dstdata[y * w + x] = srcdata[y2 * w + x2];
}
else
{
for(i = 0; i < 4; i++)
{
dstdata[4 * (y * w + x) + i]
= srcdata[4 * (y2 * w + x2) + i];
}
}
}
}

return dst;
}

pipi_image_t *pipi_sine(pipi_image_t *src, double dw, double dh,
double d, double a)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
double sina, cosa;
int x, y, w, h, i, gray;

w = src->w;
h = src->h;

gray = (src->last_modified == PIPI_PIXELS_Y_F32);

srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);
dstdata = (float *)dstp->pixels;

sina = sin(a);
cosa = cos(a);

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double angle = 2 * M_PI / dw * ((x - w / 2) * cosa
+ (y - h / 2) * sina - d);
double displacement = dh * sin(angle);
double dx, dy;
int x2, y2;

dx = -sina * displacement;
dy = cosa * displacement;

if(x < BORDER) dx = dx * x / BORDER;
if(x > w - 1 - BORDER) dx = dx * (w - 1 - x) / BORDER;
if(y < BORDER) dy = dy * y / BORDER;
if(y > h - 1 - BORDER) dy = dy * (h - 1 - y) / BORDER;

x2 = x + dx;
y2 = y + dy;

/* Just in case... */
if(x2 < 0) x2 = 0;
else if(x2 >= w) x2 = w - 1;

if(y2 < 0) y2 = 0;
else if(y2 >= h) y2 = h - 1;

if(gray)
{
dstdata[y * w + x] = srcdata[y2 * w + x2];
}
else
{
for(i = 0; i < 4; i++)
{
dstdata[4 * (y * w + x) + i]
= srcdata[4 * (y2 * w + x2) + i];
}
}
}
}

return dst;
}


+ 127
- 0
src/image/filter/yuv.cpp Voir le fichier

@@ -0,0 +1,127 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* yuv.c: YUV conversion functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_rgb2yuv(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h;

w = src->w;
h = src->h;

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

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

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double r, g, b, a, yp, u, v;
int d = 4 * (y * w + x);

r = srcdata[d];
g = srcdata[d + 1];
b = srcdata[d + 2];
a = srcdata[d + 3];

yp = 0.299 * r + 0.587 * g + 0.114 * b;
yp = (yp * 220.0 + 16.0) / 255.0;

u = 0.5 - 0.14713 * r - 0.28886 * g + 0.436 * b;
if (u < 0.0) u = 0.0;
if (u > 1.0) u = 1.0;

v = 0.5 + 0.615 * r - 0.51499 * g - 0.10001 * b;
if (v < 0.0) v = 0.0;
if (v > 1.0) v = 1.0;

dstdata[d] = yp;
dstdata[d + 1] = u;
dstdata[d + 2] = v;
dstdata[d + 3] = a;
}
}

return dst;
}

pipi_image_t *pipi_yuv2rgb(pipi_image_t *src)
{
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
int x, y, w, h;

w = src->w;
h = src->h;

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

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

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x++)
{
double r, g, b, a, yp, u, v;
int d = 4 * (y * w + x);

yp = (srcdata[d] * 255.0 - 16.0) / 220.0;
u = srcdata[d + 1] - 0.5;
v = srcdata[d + 2] - 0.5;
a = srcdata[d + 3];

r = yp + 1.13983 * v;
if (r < 0.0) r = 0.0;
if (r > 1.0) r = 1.0;
g = yp - 0.39465 * u - 0.58060 * v;
if (g < 0.0) g = 0.0;
if (g > 1.0) g = 1.0;
b = yp + 2.03211 * u;
if (b < 0.0) b = 0.0;
if (b > 1.0) b = 1.0;

dstdata[d] = r;
dstdata[d + 1] = g;
dstdata[d + 2] = b;
dstdata[d + 3] = a;
}
}

return dst;
}


+ 58
- 0
src/image/paint/bezier.cpp Voir le fichier

@@ -0,0 +1,58 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* bezier.c: bezier curves rendering functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"


int pipi_draw_bezier4(pipi_image_t *img ,
int x1, int y1,
int x2, int y2,
int x3, int y3,
int x4, int y4,
uint32_t c, int n, int aa)
{
if(img->last_modified == PIPI_PIXELS_RGBA_U8)
{
float t;
float x= x1, y= y1;
float lx, ly;
for(t=0; t<1; t+=(1.0f/n))
{
float a = t;
float b = 1 - t;

lx = x; ly = y;

x = (x1*(b*b*b)) + 3*x2*(b*b)*a + 3*x4*b*(a*a) + x3*(a*a*a);
y = (y1*(b*b*b)) + 3*y2*(b*b)*a + 3*y4*b*(a*a) + y3*(a*a*a);

pipi_draw_line(img , lx, ly, x, y, c, aa);
}
pipi_draw_line(img , x, y, x3, y3, c, aa);
}

return 0;
}

+ 318
- 0
src/image/paint/floodfill.cpp Voir le fichier

@@ -0,0 +1,318 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* floodfill.c: flood fill (4 neighbours) functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

#define STACKSIZE (1<<20)

static void pipi_flood_fill_stack_scanline_u32(pipi_pixels_t *dstp,
int x, int y,
uint32_t new, uint32_t old);
static void pipi_flood_fill_stack_scanline_float(pipi_pixels_t *dstp,
int x, int y,
float nr, float ng, float nb, float na,
float or, float og, float ob, float oa);
static int pop (int *x,int *y, int h);
static int push(int x, int y, int h);
static void clear_stack(int h);
static int stack[STACKSIZE];
static int stack_pointer;

static float get_max_color_diff(float r1, float g1, float b1,
float r2, float g2, float b2);
static int validate_pixel_f(float r1, float g1, float b1,
float r2, float g2, float b2);

/*
Stack based flood fill.
Instead of using system stack (calling recursively functions,
which can lead to big problems as this stack is a fixed and small sized one),
we create our own one.
Additionnaly, we use a scanline trick to speed up the whole thing.

This method is more or less the one described at
http://student.kuleuven.be/~m0216922/CG/floodfill.html

*/

/* Public function */
int pipi_flood_fill(pipi_image_t *src,
int px, int py,
float r, float g, float b, float a)
{
pipi_image_t *dst = src;
pipi_pixels_t *dstp;
int w, h;

if(!src) return -1;

w = src->w;
h = src->h;

if((px >= src->w) || (py >= src->h) ||
(px < 0) || (py < 0)) return -1;


if(src->last_modified == PIPI_PIXELS_RGBA_U8) {
uint32_t *dstdata;
unsigned char nr, ng, nb, na;

/* Get ARGB32 pointer */
dstp = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_U8);
dstdata = (uint32_t *)dstp->pixels;

nr = r*255.0f;
ng = g*255.0f;
nb = b*255.0f;
na = a*255.0f;

dstp->w = w;
dstp->h = h;
pipi_flood_fill_stack_scanline_u32(dstp, px, py, (na<<24)|(nr<<16)|(ng<<8)|(nb), dstdata[px+py*w]);

} else {
int gray = (dst->last_modified == PIPI_PIXELS_Y_F32);
float *dstdata;
float or, og, ob, oa;

dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32)
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32);

dstdata = (float *)dstp->pixels;

or = dstdata[(px+py*w)*4];
og = dstdata[(px+py*w)*4 + 1];
ob = dstdata[(px+py*w)*4 + 2];
oa = dstdata[(px+py*w)*4 + 3];

dstp->w = w;
dstp->h = h;

pipi_flood_fill_stack_scanline_float(dstp, px, py, r, g, b, a, or, og, ob, oa);
}

return 0;
}


/* ARGB32 */
void pipi_flood_fill_stack_scanline_u32(pipi_pixels_t *dstp,
int x, int y,
uint32_t new, uint32_t old)
{
int yt;
int left, right;

uint32_t *dstdata = (uint32_t *)dstp->pixels;

if(new==old) return;

clear_stack(dstp->h);

if(!push(x, y, dstp->h)) return;

while(pop(&x, &y, dstp->h))
{
yt = y;
while(yt >= 0 && (dstdata[x+yt*dstp->w] == old)) {
yt--;
}

yt++;
left = right = 0;

while(yt < dstp->h && (dstdata[x+yt*dstp->w] == old))
{
uint32_t *cur_line = &dstdata[(yt*dstp->w)];
int xp1 = (x+1);
int xm1 = (x-1);
cur_line[x] = new;

if(!left && x > 0 && (cur_line[xm1] == old))
{
if(!push(x - 1, yt, dstp->h)) return;
left = 1;
}
else if(left && x > 0 && (cur_line[xm1] != old))
{
left = 0;
}
if(!right && x < dstp->w - 1 && (cur_line[xp1] == old))
{
if(!push(x + 1, yt, dstp->h)) return;
right = 1;
}
else if(right && x < dstp->w - 1 && (cur_line[xp1] != old))
{
right = 0;
}
yt++;
}
}
}

/* Float version. Much slower, but supports HDR and (soon antialiasing) */
static void pipi_flood_fill_stack_scanline_float(pipi_pixels_t *dstp,
int x, int y,
float nr, float ng, float nb, float na,
float or, float og, float ob, float oa)
{
int yt;
int left, right;
float *dstdata = (float *)dstp->pixels;

if((nr==or) && (ng==og) && (nb==ob)) return;

clear_stack(dstp->h);

if(!push(x, y, dstp->h)) return;

while(pop(&x, &y, dstp->h))
{
yt = y;
while(yt >= 0 && validate_pixel_f(dstdata[(x+yt*dstp->w)*4] ,
dstdata[(x+yt*dstp->w)*4 + 1] ,
dstdata[(x+yt*dstp->w)*4 + 2] ,
or, og, ob)) {
yt--;
}

yt++;
left = right = 0;

while(yt < dstp->h && validate_pixel_f(dstdata[(x+yt*dstp->w)*4] ,
dstdata[(x+yt*dstp->w)*4 + 1] ,
dstdata[(x+yt*dstp->w)*4 + 2] ,
or, og, ob))
{
float *cur_line = &dstdata[(yt*dstp->w)*4];
int xp1 = (x+1)*4;
int xm1 = (x-1)*4;
cur_line[x*4] = nr;
cur_line[x*4 + 1] = ng;
cur_line[x*4 + 2] = nb;
cur_line[x*4 + 3] = na;

if(!left && x > 0 && validate_pixel_f(cur_line[xm1],
cur_line[xm1 + 1],
cur_line[xm1 + 2],
or, og, ob))
{
if(!push(x - 1, yt, dstp->h)) return;
left = 1;
}
else if(left && x > 0 && !validate_pixel_f(cur_line[xm1] ,
cur_line[xm1 + 1] ,
cur_line[xm1 + 2] ,
or, og, ob))
{
left = 0;
}
if(!right && x < dstp->w - 1 && validate_pixel_f(cur_line[xp1] ,
cur_line[xp1 + 1] ,
cur_line[xp1 + 2] ,
or, og, ob))
{
if(!push(x + 1, yt, dstp->h)) return;
right = 1;
}
else if(right && x < dstp->w - 1 && !validate_pixel_f(cur_line[xp1] ,
cur_line[xp1 + 1] ,
cur_line[xp1 + 2],
or, og, ob))
{
right = 0;
}
yt++;
}
}
}


/* Following functions are local */

static int pop(int *x, int *y, int h)
{
if(stack_pointer > 0)
{
int p = stack[stack_pointer];
*x = p / h;
*y = p % h;
stack_pointer--;
return 1;
}
else
{
return 0;
}
}

static int push(int x, int y, int h)
{
if(stack_pointer < STACKSIZE - 1)
{
stack_pointer++;
stack[stack_pointer] = h * x + y;
return 1;
}
else
{
return 0;
}
}

static void clear_stack(int h)
{
int x, y;
while(pop(&x, &y, h));
}

#define MAX(a, b) (b>a?b:a)


/* FIXME : Paves the way to antialiasing, but could be only 3 tests */
static float get_max_color_diff(float r1, float g1, float b1,
float r2, float g2, float b2)
{
float r = abs(r2-r1);
float g = abs(g2-g1);
float b = abs(b2-b1);

return MAX(MAX(r, g), b);
}


static int validate_pixel_f(float r1, float g1, float b1,
float r2, float g2, float b2)
{

if(get_max_color_diff(r1, g1, b1,
r2, g2, b2)
== 0) return 1;
else
return 0;
}

+ 470
- 0
src/image/paint/line.cpp Voir le fichier

@@ -0,0 +1,470 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* line.c: line rendering functions
*/

#include "config.h"

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

#include <math.h>

#include "pipi.h"
#include "pipi-internals.h"

#if !defined TEMPLATE_FILE /* This file uses the template system */

static float fractf(float d) { return (d - floorf(d)); }
static float fractinvf(float d) { return (1 - (d - floorf(d))); }

struct line
{
int xa, ya;
int xb, yb;
void (*draw) (pipi_image_t*, struct line*);
union {
uint32_t color32;
float colorf[3];
};

union {
uint32_t *buf_u32;
float *buf_f;
};

};

#define TEMPLATE_FLAGS SET_FLAG_GRAY | SET_FLAG_8BIT
#define TEMPLATE_FILE "paint/line.c"
#include "pipi-template.h"

static void clip_line(pipi_image_t*, struct line*);
static uint8_t clip_bits(pipi_image_t*, int, int);

int pipi_draw_line(pipi_image_t *img , int xa, int ya, int xb, int yb, uint32_t c, int aa)
{
struct line s;
s.xa = xa;
s.ya = ya;
s.xb = xb;
s.yb = yb;


/* No Transparency routine for u32 yet, fallback to float version */
if(img->last_modified == PIPI_PIXELS_RGBA_U8)
{
if(!aa)
{
uint32_t *dstdata;
dstdata = (uint32_t *)pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8)->pixels;
s.color32 = c;
s.buf_u32 = dstdata;
s.draw = line_8bit;
}
else
{
float *dstdata;
dstdata = (float *)pipi_get_pixels(img, PIPI_PIXELS_RGBA_F32)->pixels;
s.colorf[2] = ((c&0x00FF0000)>>16)/255.0f; /* XXX FIXME */
s.colorf[1] = ((c&0x0000FF00)>>8)/255.0f; /* XXX FIXME */
s.colorf[0] = (c&0x000000FF)/255.0f; /* XXX FIXME */
s.buf_f = dstdata;
s.draw = aaline;
}
}
else if(img->last_modified == PIPI_PIXELS_Y_F32)
{
float *dstdata;
dstdata = (float *)pipi_get_pixels(img, PIPI_PIXELS_Y_F32)->pixels;
s.colorf[0] = (c & 0xff) / 255.0f; /* XXX FIXME */
s.buf_f = dstdata;
s.draw = aa ? aaline_gray : line_gray;
}
else
{
float *dstdata;
dstdata = (float *)pipi_get_pixels(img, PIPI_PIXELS_RGBA_F32)->pixels;
s.colorf[2] = ((c&0x00FF0000)>>16)/255.0f; /* XXX FIXME */
s.colorf[1] = ((c&0x0000FF00)>>8)/255.0f; /* XXX FIXME */
s.colorf[0] = (c&0x000000FF)/255.0f; /* XXX FIXME */
s.buf_f = dstdata;
s.draw = aa ? aaline : line;
}

clip_line(img, &s);
return 0;
}

int pipi_draw_rectangle(pipi_image_t *img , int xa, int ya, int xb, int yb, uint32_t c, int aa)
{
while(ya < yb)
{
pipi_draw_line(img, xa, ya, xb, ya, c, aa);
ya++;
}

while(ya > yb)
{
pipi_draw_line(img, xa, ya, xb, ya, c, aa);
ya--;
}

return pipi_draw_line(img, xa, yb, xb, yb, c, aa);
}

int pipi_draw_polyline(pipi_image_t *img, int const x[], int const y[],
int n, uint32_t c, int aa)
{
int i;

for(i = 0; i < n; i++)
pipi_draw_line(img, x[i], y[i], x[i + 1], y[i + 1], c, aa);

return 0;
}

/*
* XXX: The following functions are local.
*/

/* Generic Cohen-Sutherland line clipping function. */
static void clip_line(pipi_image_t *img, struct line* s)
{
uint8_t bits1, bits2;

bits1 = clip_bits(img, s->xa, s->ya);
bits2 = clip_bits(img, s->xb, s->yb);

if(bits1 & bits2)
return;

if(bits1 == 0)
{
if(bits2 == 0)
s->draw(img, s);
else
{
int tmp;
tmp = s->xa; s->xa = s->xb; s->xb = tmp;
tmp = s->ya; s->ya = s->yb; s->yb = tmp;
clip_line(img, s);
}
return;
}

if(bits1 & (1<<0))
{
s->ya = s->yb - (s->xb - 0) * (s->yb - s->ya) / (s->xb - s->xa);
s->xa = 0;
}
else if(bits1 & (1<<1))
{
int xmax = img->w - 1;
s->ya = s->yb - (s->xb - xmax) * (s->yb - s->ya) / (s->xb - s->xa);
s->xa = xmax;
}
else if(bits1 & (1<<2))
{
s->xa = s->xb - (s->yb - 0) * (s->xb - s->xa) / (s->yb - s->ya);
s->ya = 0;
}
else if(bits1 & (1<<3))
{
int ymax = img->h - 1;
s->xa = s->xb - (s->yb - ymax) * (s->xb - s->xa) / (s->yb - s->ya);
s->ya = ymax;
}

clip_line(img, s);
}

/* Helper function for clip_line(). */
static uint8_t clip_bits(pipi_image_t *img, int x, int y)
{
uint8_t b = 0;

if(x < 0)
b |= (1<<0);
else if(x >= (int)img->w)
b |= (1<<1);

if(y < 0)
b |= (1<<2);
else if(y >= (int)img->h)
b |= (1<<3);

return b;
}

#else /* XXX: the following functions use the template system */

/* Xiaolin Wu's line algorithm, as seen at http://portal.acm.org/citation.cfm?id=122734 */

#define PLOT(x, y, c) \
if(FLAG_8BIT) \
{ \
/* TODO */ \
} \
else \
{ \
if(FLAG_GRAY) \
{ \
s->buf_f[((int)(x))+((int)(y))*img->w] = \
(c*s->colorf[0]) + (1-c) * s->buf_f[((int)(x))+((int)(y))*img->w]; \
if(s->buf_f[((int)(x))+((int)(y))*img->w] > 1.0f) \
s->buf_f[((int)(x))+((int)(y))*img->w] = 1.0f; \
if(s->buf_f[((int)(x))+((int)(y))*img->w] < 0.0f) \
s->buf_f[((int)(x))+((int)(y))*img->w] = 0.0f; \
} \
else \
{ \
int qwer = (((int)(x)*4))+((int)(y))*(img->w*4);\
int qweg = (1+((int)(x)*4))+((int)(y))*(img->w*4); \
int qweb = (2+((int)(x)*4))+((int)(y))*(img->w*4); \
s->buf_f[qwer] = (c*s->colorf[0]) + (1-c) * s->buf_f[qwer]; \
s->buf_f[qweg] = (c*s->colorf[1]) + (1-c) * s->buf_f[qweg]; \
s->buf_f[qweb] = (c*s->colorf[2]) + (1-c) * s->buf_f[qweb]; \
if(s->buf_f[qwer] > 1.0f) s->buf_f[qwer] = 1.0f; \
if(s->buf_f[qwer] < 0.0f) s->buf_f[qwer] = 0.0f; \
if(s->buf_f[qweg] > 1.0f) s->buf_f[qweg] = 1.0f; \
if(s->buf_f[qweg] < 0.0f) s->buf_f[qweg] = 0.0f; \
if(s->buf_f[qweb] > 1.0f) s->buf_f[qweb] = 1.0f; \
if(s->buf_f[qweb] < 0.0f) s->buf_f[qweb] = 0.0f; \
} \
}

static void T(aaline)(pipi_image_t *img, struct line* s)
{
float xa = s->xa, ya = s->ya, xb = s->xb, yb = s->yb;
float g, xd, yd, xgap, xend, yend, xf, yf, val1, val2;
int x, y, ixa, ixb, iya, iyb;

xd = xb - xa;
yd = yb - ya;

/* "Horizontal" line (X greater than Y)*/
if (fabsf(xd) > fabsf(yd))
{
if (xa > xb)
{
float tmp;
tmp = xa; xa = xb; xb = tmp;
tmp = ya; ya = yb; yb = tmp;
xd = (xb-xa);
yd = (yb-ya);
}
g = yd/xd;

xend = (float)(int)(xa+0.5);
yend = ya + g*(xend-xa);
xgap = fractinvf(xa+0.5);
ixa = (int)xend;
iya = (int)yend;
val1 = fractinvf(yend)*xgap;
val2 = fractf(yend)*xgap;

PLOT(ixa, iya, val1);
PLOT(ixa, (iya+1)<ya?(iya+1):iya, val2);

yf = yend+g;
xend = (float)(int)(xb+0.5);
yend = yb + g*(xend-xb);
xgap = fractinvf(xb-0.5);
ixb = (int)xend;
iyb = (int)yend;
val1 = fractinvf(yend)*xgap;
val2 = fractf(yend)*xgap;

PLOT(ixb, iyb, val1);
PLOT(ixb, iyb+1<yb?iyb+1:iyb, val2);

for (x = (ixa+1); x < ixb; x++)
{
float focus;

val1 = fractinvf(yf);
val2 = fractf(yf);
focus = (1.0 - fabsf(val1-val2));
val1 += 0.3*focus;
val2 += 0.3*focus;

PLOT(x, yf, val1);
PLOT(x, (yf+1)<ya?(yf+1):yf, val2);

yf = yf + g;
}
}
/* "Vertical" line (Y greater than X)*/
else
{
if (xa > xb)
{
float tmp;
tmp = xa; xa = xb; xb = tmp;
tmp = ya; ya = yb; yb = tmp;
xd = (xb-xa);
yd = (yb-ya);
}

g = xd/yd;

xend = (float)(int)(xa+0.5);
yend = ya + g*(xend-xa);
xgap = fractf(xa+0.5);
ixa = (int)xend;
iya = (int)yend;
val1 = fractinvf(yend)*xgap;
val2 = fractf(yend)*xgap;

PLOT(ixa, iya, val1);
PLOT(ixa, (iya+1)<ya?(iya+1):iya, val2);

xf = xend + g;

xend = (float)(int)(xb+0.5);
yend = yb + g*(xend-xb);
xgap = fractinvf(xb-0.5);
ixb = (int)xend;
iyb = (int)yend;
val1 = fractinvf(yend)*xgap;
val2 = fractf(yend)*xgap;

PLOT(ixb, iyb, val1);
PLOT(ixb, (iyb+1)<yb?(iyb+1):iyb, val2);


for (y = (iya+1); y < iyb; y++)
{
float focus;
int vx = xf;
val1 = fractinvf(xf);
val2 = fractf(xf);
focus = (1.0 - fabsf(val1-val2));
val1 += 0.3*focus;
val2 += 0.3*focus;
PLOT(vx, y, val1);
vx++;
PLOT(vx, y, val2);
xf = xf + g;
}
}
}

#undef PLOT

/* Solid line drawing function, using Bresenham's mid-point line
* scan-conversion algorithm. */
static void T(line)(pipi_image_t *img, struct line* s)
{
int xa, ya, xb, yb;
int dx, dy;
int xinc, yinc;

xa = s->xa; ya = s->ya; xb = s->xb; yb = s->yb;

dx = abs(xb - xa);
dy = abs(yb - ya);

xinc = (xa > xb) ? -1 : 1;
yinc = (ya > yb) ? -1 : 1;

if(dx >= dy)
{
int dpr = dy << 1;
int dpru = dpr - (dx << 1);
int delta = dpr - dx;

for(; dx >= 0; dx--)
{
if(FLAG_GRAY)
{
if(FLAG_8BIT)
/* TODO */;
else
s->buf_f[xa + ya * img->w] = s->colorf[0];
}
else
{
if(FLAG_8BIT)
s->buf_u32[xa + ya * img->w] = s->color32;
else
{
s->buf_f[4 * (ya * img->w + xa)] = s->colorf[0];
s->buf_f[4 * (ya * img->w + xa) + 1] = s->colorf[1];
s->buf_f[4 * (ya * img->w + xa) + 2] = s->colorf[2];
}
}

if(delta > 0)
{
xa += xinc;
ya += yinc;
delta += dpru;
}
else
{
xa += xinc;
delta += dpr;
}
}
}
else
{
int dpr = dx << 1;
int dpru = dpr - (dy << 1);
int delta = dpr - dy;

for(; dy >= 0; dy--)
{
if(FLAG_GRAY)
{
if(FLAG_8BIT)
/* TODO */;
else
s->buf_f[xa + ya * img->w] = s->colorf[0];
}
else
{
if(FLAG_8BIT)
s->buf_u32[xa + ya * img->w] = s->color32;
else
{
s->buf_f[4 * (ya * img->w + xa)] = s->colorf[0];
s->buf_f[4 * (ya * img->w + xa) + 1] = s->colorf[1];
s->buf_f[4 * (ya * img->w + xa) + 2] = s->colorf[2];
}
}

if(delta > 0)
{
xa += xinc;
ya += yinc;
delta += dpru;
}
else
{
ya += yinc;
delta += dpr;
}
}
}
}

#endif


+ 47
- 0
src/image/paint/rectangle.cpp Voir le fichier

@@ -0,0 +1,47 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.
*/

/*
* rectangle.c: rectangle rendering functions
*/

/* XXX: this file is a JOKE, don't use it */

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

int pipi_draw_rectangle(pipi_image_t *img , int xa, int ya, int xb, int yb, uint32_t c, int aa)
{
while(ya < yb)
{
pipi_draw_line(img, xa, ya, xb, ya, c, aa);
ya++;
}

while(ya > yb)
{
pipi_draw_line(img, xa, ya, xb, ya, c, aa);
ya--;
}

return pipi_draw_line(img, xa, yb, xb, yb, c, aa);
}


+ 56
- 0
src/image/paint/tile.cpp Voir le fichier

@@ -0,0 +1,56 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* tile.c: Tiling function
*/

#include "config.h"

#include <string.h>

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_tile(pipi_image_t *tile, int w, int h)
{
pipi_image_t *dst;
pipi_pixels_t *tilep, *dstp;
float *tiledata, *dstdata;
int x, y, tw, th, todo;

tw = tile->w;
th = tile->h;

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

tilep = pipi_get_pixels(tile, PIPI_PIXELS_RGBA_F32);
tiledata = (float *)tilep->pixels;

for(y = 0; y < h; y++)
{
for(x = 0; x < w; x += todo)
{
todo = (x + tw > w) ? w - x : tw;
memcpy(dstdata + 4 * (y * w + x),
tiledata + 4 * (y % th) * tw,
4 * todo * sizeof(float));
}
}

return dst;
}


+ 138
- 0
src/image/pipi-internals.h Voir le fichier

@@ -0,0 +1,138 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2009 Sam Hocevar <sam@hocevar.net>
* 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-internals.h: internal types
*/

#ifndef __PIPI_INTERNALS_H__
#define __PIPI_INTERNALS_H__

#include "pipi-stubs.h"

#define SET_FLAG_GRAY 0x00000001
#define SET_FLAG_WRAP 0x00000002
#define SET_FLAG_8BIT 0x00000004

struct pipi_histogram
{
int r_present, g_present, b_present, y_present;
unsigned int a[256];
unsigned int r[256];
unsigned int g[256];
unsigned int b[256];
unsigned int y[256];
};

#ifdef USE_TILES
#define TILE_SIZE 128

struct pipi_tile
{
int x, y;
int zoom;

int refcount;

pipi_format_t fmt;
int plane;
union { uint8_t *u8; float *f; double *d; } data;
union { uint8_t u8[1]; float f[1]; double d[1]; } align;
};
#endif /* USE_TILES */

/* 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 w, h, pitch;

#ifdef USE_TILES
pipi_tile_t **tiles;
int ntiles;
#endif /* USE_TILES */

/* A list of internal image flags.
* wrap: should filters wrap around at edges?
* u8: are the image samples still 8-bit per channel? */
int wrap, u8;

/* Translation vectors for wrap around and tiling. */
int wrapx1, wrapy1, wrapx2, wrapy2;

/* List of all possible pixel formats and the last active one. */
pipi_pixels_t p[PIPI_PIXELS_MAX];
pipi_format_t last_modified;

/* Private data used by the codec */
pipi_format_t codec_format;
void *codec_priv;
int (*codec_free)(pipi_image_t *);
};

struct pipi_sequence
{
int w, h, fps;
uint8_t *convert_buf;

void *codec_priv;
};

struct pipi_context
{
int nimages;
pipi_image_t *images[1024]; /* FIXME: do dynamic allocation */
};

#ifdef USE_IMLIB2
pipi_image_t *pipi_load_imlib2(const char *name);
int pipi_save_imlib2(pipi_image_t *img, const char *name);
#endif

#ifdef USE_OPENCV
pipi_image_t *pipi_load_opencv(const char *name);
int pipi_save_opencv(pipi_image_t *img, const char *name);
#endif

#ifdef USE_SDL
pipi_image_t *pipi_load_sdl(const char *name);
int pipi_save_sdl(pipi_image_t *img, const char *name);
#endif

#ifdef USE_GDIPLUS
pipi_image_t *pipi_load_gdiplus(const char *name);
int pipi_save_gdiplus(pipi_image_t *img, const char *name);
#endif

#ifdef USE_GDI
pipi_image_t *pipi_load_gdi(const char *name);
int pipi_save_gdi(pipi_image_t *img, const char *name);
#endif

#ifdef USE_COCOA
pipi_image_t *pipi_load_coreimage(const char *name);
int pipi_save_coreimage(pipi_image_t *img, const char *name);
#endif

#ifdef USE_JPEG
pipi_image_t *pipi_load_jpeg(const char *name);
int pipi_save_jpeg(pipi_image_t *img, const char *name);
#endif

pipi_image_t *pipi_load_oric(const char *name);
int pipi_save_oric(pipi_image_t *img, const char *name);

#endif /* __PIPI_INTERNALS_H__ */


+ 97
- 0
src/image/pipi-stubs.h Voir le fichier

@@ -0,0 +1,97 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
* All Rights Reserved
*
* $Id$
*
* This library is free software; 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.
*/

/*
* This file contains replacements for commonly found object types and
* function prototypes that are sometimes missing.
*/

#ifndef __PIPI_STUBS_H__
#define __PIPI_STUBS_H__

/* errno handling */
#if defined HAVE_ERRNO_H && !defined __KERNEL__
# include <errno.h>
static inline void seterrno(int e) { errno = e; }
static inline int geterrno(void) { return errno; }
#else
# define seterrno(x) do { (void)(x); } while(0)
# define geterrno(x) 0
#endif

/* debug messages */
#if defined DEBUG && !defined __KERNEL__
# include <stdio.h>
# include <stdarg.h>
static inline void debug(const char *format, ...)
{
int saved_errno = geterrno();
va_list args;
va_start(args, format);
fprintf(stderr, "** libpipi debug ** ");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
va_end(args);
seterrno(saved_errno);
}
#else
# define debug(format, ...) do {} while(0)
#endif

/* hton16() and hton32() */
#if defined HAVE_HTONS && !defined __KERNEL__
# if defined HAVE_ARPA_INET_H
# include <arpa/inet.h>
# elif defined HAVE_NETINET_IN_H
# include <netinet/in.h>
# endif
# define hton16 htons
# define hton32 htonl
#else
# if defined __KERNEL__
/* Nothing to do */
# elif defined HAVE_ENDIAN_H
# include <endian.h>
# endif
static inline uint16_t hton16(uint16_t x)
{
/* This is compile-time optimised with at least -O1 or -Os */
#if defined HAVE_ENDIAN_H
if(__BYTE_ORDER == __BIG_ENDIAN)
#else
uint32_t const dummy = 0x12345678;
if(*(uint8_t const *)&dummy == 0x12)
#endif
return x;
else
return (x >> 8) | (x << 8);
}

static inline uint32_t hton32(uint32_t x)
{
/* This is compile-time optimised with at least -O1 or -Os */
#if defined HAVE_ENDIAN_H
if(__BYTE_ORDER == __BIG_ENDIAN)
#else
uint32_t const dummy = 0x12345678;
if(*(uint8_t const *)&dummy == 0x12)
#endif
return x;
else
return (x >> 24) | ((x >> 8) & 0x0000ff00)
| ((x << 8) & 0x00ff0000) | (x << 24);
}
#endif

#endif /* __PIPI_STUBS_H__ */


+ 80
- 0
src/image/pipi-template.h Voir le fichier

@@ -0,0 +1,80 @@
/*
* libpipi Pathetic image processing interface 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-template.h: the magic template preprocessing file
*
* Define the following macros before including this file:
* * TEMPLATE_FLAGS is set to a list of toggle flags, a binary OR of:
* - SET_FLAG_GRAY
* - SET_FLAG_WRAP
* - SET_FLAG_8BIT
* * TEMPLATE_FILE is set to the template file. The following macros
* will be defined when including it. Their value depend on the flags
* specified above:
* - FLAG_GRAY is set to 0 or 1
* - FLAG_WRAP is set to 0 or 1
* - FLAG_8BIT is set to 0 or 1
* - T(x) expands x by adding relevant information, eg. x##_gray_wrap
*/

#if !defined FLAG_GRAY
# if (TEMPLATE_FLAGS) & SET_FLAG_GRAY
# define FLAG_GRAY 1
# define T_GRAY(x) CAT(x, _gray)
# include __FILE__
# undef FLAG_GRAY
# undef T_GRAY
# endif
# define FLAG_GRAY 0
# define T_GRAY(x) x
# include __FILE__
# undef FLAG_GRAY
# undef T_GRAY

#elif !defined FLAG_WRAP
# if (TEMPLATE_FLAGS) & SET_FLAG_WRAP
# define FLAG_WRAP 1
# define T_WRAP(x) CAT(x, _wrap)
# include __FILE__
# undef FLAG_WRAP
# undef T_WRAP
# endif
# define FLAG_WRAP 0
# define T_WRAP(x) x
# include __FILE__
# undef FLAG_WRAP
# undef T_WRAP

#elif !defined FLAG_8BIT
# if (TEMPLATE_FLAGS) & SET_FLAG_8BIT
# define FLAG_8BIT 1
# define T_8BIT(x) CAT(x, _8bit)
# include __FILE__
# undef FLAG_8BIT
# undef T_8BIT
# endif
# define FLAG_8BIT 0
# define T_8BIT(x) x
# include __FILE__
# undef FLAG_8BIT
# undef T_8BIT

#else
# define CAT(x, y) x ## y
# define T(x) T_8BIT(T_WRAP(T_GRAY(x)))
# include TEMPLATE_FILE
# undef CAT
# undef S
#endif


+ 100
- 0
src/image/pipi-types.h Voir le fichier

@@ -0,0 +1,100 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2008 Sam Hocevar <sam@zoy.org>
* All Rights Reserved
*
* $Id$
*
* This library is free software; 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.
*/

/*
* This file contains definitions for the C99 integer types.
*/

#ifndef __PIPI_TYPES_H__
#define __PIPI_TYPES_H__

#ifndef PIPI_TYPES
# define PIPI_TYPES 1
#endif

/* mode 1: standard <stdint.h> header is present, just include it */
#if PIPI_TYPES == 1
# include <stdint.h>
# include <unistd.h>

/* mode 2: standard <inttypes.h> header is present, just include it */
#elif PIPI_TYPES == 2
# include <inttypes.h>
# include <unistd.h>

/* mode 3: <windows.h> indicates Win32, only (u)intptr_t is present
* FIXME: Win64 probably doesn't work that way */
#elif PIPI_TYPES == 3
#include <windows.h>

typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int int64_t;

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long int uint64_t;

typedef int ssize_t;
typedef unsigned int size_t;

/* fallback: nothing is known, we assume the platform is 32-bit and
* sizeof(long) == sizeof(void *). We don't typedef directly because we
* have no idea what other typedefs have already been made. */
#else
typedef signed char _pipi_int8_t;
typedef signed short _pipi_int16_t;
typedef signed long int _pipi_int32_t;
typedef signed long long int _pipi_int64_t;
# undef int8_t
# define int8_t _pipi_int8_t
# undef int16_t
# define int16_t _pipi_int16_t
# undef int32_t
# define int32_t _pipi_int32_t
# undef int64_t
# define int64_t _pipi_int64_t

typedef unsigned char _pipi_uint8_t;
typedef unsigned short _pipi_uint16_t;
typedef unsigned long int _pipi_uint32_t;
typedef unsigned long long int _pipi_uint64_t;
# undef uint8_t
# define uint8_t _pipi_uint8_t
# undef uint16_t
# define uint16_t _pipi_uint16_t
# undef uint32_t
# define uint32_t _pipi_uint32_t
# undef uint64_t
# define uint64_t _pipi_uint64_t

typedef long int _pipi_intptr_t;
typedef unsigned long int _pipi_uintptr_t;
# undef intptr_t
# define intptr_t _pipi_intptr_t
# undef uintptr_t
# define uintptr_t _pipi_uintptr_t

typedef int _pipi_ssize_t;
typedef unsigned int _pipi_size_t;
# undef ssize_t
# define ssize_t _pipi_ssize_t
# undef size_t
# define size_t _pipi_size_t

#endif

#endif /* __PIPI_TYPES_H__ */


+ 96
- 0
src/image/pipi.cpp Voir le fichier

@@ -0,0 +1,96 @@
/*
* libpipi Pathetic image processing interface 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "pipi.h"
#include "pipi-internals.h"

/** \brief Return the \e libpipi version.
*
* Return a read-only string with the \e libpipi version information.
*
* This function never fails.
*
* \return The \e libpipi version information.
*/
char const * pipi_get_version(void)
{
return PACKAGE_VERSION;
}

/*
static int init = 0;

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

_pipi_init_pixels();
}
*/

pipi_image_t *pipi_new(int w, int h)
{
pipi_image_t *img;

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

img->w = w;
img->h = h;
img->last_modified = PIPI_PIXELS_UNINITIALISED;
img->codec_format = PIPI_PIXELS_UNINITIALISED;

img->wrap = 0;
img->u8 = 1;

return img;
}

pipi_image_t *pipi_copy(pipi_image_t *src)
{
pipi_image_t *dst = pipi_new(src->w, src->h);

/* Copy properties */
dst->wrap = src->wrap;
dst->u8 = src->u8;

/* Copy pixels, if any */
if(src->last_modified != PIPI_PIXELS_UNINITIALISED)
{
pipi_pixels_t *srcp, *dstp;

srcp = &src->p[src->last_modified];
dstp = &dst->p[src->last_modified];

memcpy(dstp, srcp, sizeof(pipi_pixels_t));
dstp->pixels = malloc(dstp->bytes);
memcpy(dstp->pixels, srcp->pixels, dstp->bytes);

dst->last_modified = src->last_modified;
}

return dst;
}


+ 253
- 0
src/image/pipi.h Voir le fichier

@@ -0,0 +1,253 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2009 Sam Hocevar <sam@hocevar.net>
* 2008 Jean-Yves Lamoureux <jylam@lnxscene.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.h: the full libpipi public API
*/

#ifndef __PIPI_H__
#define __PIPI_H__

#include <pipi-types.h>

#undef __extern
#if defined(_DOXYGEN_SKIP_ME)
#elif defined(_WIN32) && defined(__LIBPIPI__)
# define __extern extern __declspec(dllexport)
#else
# define __extern extern
#endif

#ifdef __cplusplus
extern "C"
{
#endif

/* pipi_scan_t: this enum is a list of all possible scanning methods when
* parsing an image’s pixels. Not all functions support all scanning paths. */
typedef enum
{
PIPI_SCAN_RASTER = 0,
PIPI_SCAN_SERPENTINE = 1
}
pipi_scan_t;

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

PIPI_PIXELS_RGBA_U8 = 0,
PIPI_PIXELS_BGR_U8 = 1,
PIPI_PIXELS_RGBA_F32 = 2,
PIPI_PIXELS_Y_F32 = 3,

PIPI_PIXELS_MASK_U8 = 4,

PIPI_PIXELS_MAX = 5
}
pipi_format_t;


typedef enum
{
PIPI_COLOR_R = 1,
PIPI_COLOR_G = 2,
PIPI_COLOR_B = 4,
PIPI_COLOR_A = 8,
PIPI_COLOR_Y = 16
}
pipi_color_flag_t;

struct pixel_u32
{
uint8_t r, g, b, a;
};
struct pixel_float
{
float r, g, b, a;
};

typedef struct
{
union
{
struct pixel_float pixel_float;
struct pixel_u32 pixel_u32;
};
}
pipi_pixel_t;

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

/* pipi_tile_t: the internal tile type */
typedef struct pipi_tile pipi_tile_t;

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

/* pipi_sequence_t: the image sequence type */
typedef struct pipi_sequence pipi_sequence_t;

/* pipi_context_t: the processing stack */
typedef struct pipi_context pipi_context_t;

/* pipi_histogram_t: the histogram type */
typedef struct pipi_histogram pipi_histogram_t;

/* pipi_command_t: the command type */
typedef struct
{
char const *name;
int argc;
}
pipi_command_t;

__extern pipi_pixel_t *pipi_get_color_from_string(const char* s);

__extern char const * pipi_get_version(void);

__extern pipi_context_t *pipi_create_context(void);
__extern void pipi_destroy_context(pipi_context_t *);
__extern pipi_command_t const *pipi_get_command_list(void);
__extern int pipi_command(pipi_context_t *, char const *, ...);

__extern pipi_tile_t *pipi_get_tile(pipi_image_t *, int, int, int,
pipi_format_t, int);
__extern void pipi_release_tile(pipi_image_t *, pipi_tile_t *);
__extern pipi_tile_t *pipi_create_tile(pipi_format_t, int);

__extern pipi_image_t *pipi_load(char const *);
__extern pipi_image_t *pipi_load_stock(char const *);
__extern pipi_image_t *pipi_new(int, int);
__extern pipi_image_t *pipi_copy(pipi_image_t *);
__extern void pipi_free(pipi_image_t *);
__extern int pipi_save(pipi_image_t *, const char *);

__extern void pipi_set_gamma(double);
__extern pipi_pixels_t *pipi_get_pixels(pipi_image_t *, pipi_format_t);
__extern void pipi_release_pixels(pipi_image_t *, pipi_pixels_t *);
__extern void pipi_set_colorspace(pipi_image_t *, pipi_format_t);
__extern int pipi_get_image_width(pipi_image_t *img);
__extern int pipi_get_image_height(pipi_image_t *img);
__extern int pipi_get_image_pitch(pipi_image_t *img);
__extern int pipi_get_image_last_modified(pipi_image_t *img);
__extern const char* pipi_get_format_name(int format);


__extern double pipi_measure_msd(pipi_image_t *, pipi_image_t *);
__extern double pipi_measure_rmsd(pipi_image_t *, pipi_image_t *);

__extern pipi_image_t *pipi_resize_bresenham(pipi_image_t *, int, int);
__extern pipi_image_t *pipi_resize_bicubic(pipi_image_t *, int, int);
__extern pipi_image_t *pipi_crop(pipi_image_t *, int, int, int, int);

__extern pipi_image_t *pipi_render_random(int, int);
__extern pipi_image_t *pipi_render_bayer(int, int);
__extern pipi_image_t *pipi_render_halftone(int, int);

__extern pipi_image_t *pipi_rgb(pipi_image_t *, pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_red(pipi_image_t *);
__extern pipi_image_t *pipi_green(pipi_image_t *);
__extern pipi_image_t *pipi_blue(pipi_image_t *);
__extern pipi_image_t *pipi_blit(pipi_image_t *, pipi_image_t *, int, int);
__extern pipi_image_t *pipi_merge(pipi_image_t *, pipi_image_t *, double);
__extern pipi_image_t *pipi_mean(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_min(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_max(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_add(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_sub(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_difference(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_multiply(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_divide(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_screen(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_overlay(pipi_image_t *, pipi_image_t *);

__extern pipi_image_t *pipi_convolution(pipi_image_t *, int, int, double[]);
__extern pipi_image_t *pipi_gaussian_blur(pipi_image_t *, float);
__extern pipi_image_t *pipi_gaussian_blur_ext(pipi_image_t *,
float, float, float, float, float);
__extern pipi_image_t *pipi_box_blur(pipi_image_t *, int);
__extern pipi_image_t *pipi_box_blur_ext(pipi_image_t *, int, int);
__extern pipi_image_t *pipi_brightness(pipi_image_t *, double);
__extern pipi_image_t *pipi_contrast(pipi_image_t *, double);
__extern pipi_image_t *pipi_autocontrast(pipi_image_t *);
__extern pipi_image_t *pipi_invert(pipi_image_t *);
__extern pipi_image_t *pipi_threshold(pipi_image_t *, double);
__extern pipi_image_t *pipi_hflip(pipi_image_t *);
__extern pipi_image_t *pipi_vflip(pipi_image_t *);
__extern pipi_image_t *pipi_rotate(pipi_image_t *, double);
__extern pipi_image_t *pipi_rotate90(pipi_image_t *);
__extern pipi_image_t *pipi_rotate180(pipi_image_t *);
__extern pipi_image_t *pipi_rotate270(pipi_image_t *);
__extern pipi_image_t *pipi_median(pipi_image_t *, int);
__extern pipi_image_t *pipi_median_ext(pipi_image_t *, int, int);
__extern pipi_image_t *pipi_dilate(pipi_image_t *);
__extern pipi_image_t *pipi_erode(pipi_image_t *);
__extern pipi_image_t *pipi_sine(pipi_image_t *, double, double,
double, double);
__extern pipi_image_t *pipi_wave(pipi_image_t *, double, double,
double, double);
__extern pipi_image_t *pipi_rgb2yuv(pipi_image_t *);
__extern pipi_image_t *pipi_yuv2rgb(pipi_image_t *);

__extern pipi_image_t *pipi_order(pipi_image_t *);

__extern pipi_image_t *pipi_tile(pipi_image_t *, int, int);
__extern int pipi_flood_fill(pipi_image_t *,
int, int, float, float, float, float);
__extern int pipi_draw_line(pipi_image_t *, int, int, int, int, uint32_t, int);
__extern int pipi_draw_rectangle(pipi_image_t *, int, int, int, int, uint32_t, int);
__extern int pipi_draw_polyline(pipi_image_t *, int const[], int const[],
int , uint32_t, int);
__extern int pipi_draw_bezier4(pipi_image_t *,int, int, int, int, int, int, int, int, uint32_t, int, int);
__extern pipi_image_t *pipi_reduce(pipi_image_t *, int, double const *);

__extern pipi_image_t *pipi_dither_ediff(pipi_image_t *, pipi_image_t *,
pipi_scan_t);
__extern pipi_image_t *pipi_dither_ordered(pipi_image_t *, pipi_image_t *);
__extern pipi_image_t *pipi_dither_ordered_ext(pipi_image_t *, pipi_image_t *,
double, double);
__extern pipi_image_t *pipi_dither_halftone(pipi_image_t *, double, double);
__extern pipi_image_t *pipi_dither_random(pipi_image_t *);
__extern pipi_image_t *pipi_dither_ostromoukhov(pipi_image_t *, pipi_scan_t);
__extern pipi_image_t *pipi_dither_dbs(pipi_image_t *);
__extern void pipi_dither_24to16(pipi_image_t *);

__extern pipi_histogram_t* pipi_new_histogram(void);
__extern int pipi_get_image_histogram(pipi_image_t *, pipi_histogram_t *, int);
__extern int pipi_free_histogram(pipi_histogram_t*);
__extern int pipi_render_histogram(pipi_image_t *, pipi_histogram_t*, int);

__extern pipi_sequence_t *pipi_open_sequence(char const *, int, int, int,
int, int, int, int);
__extern int pipi_feed_sequence(pipi_sequence_t *, uint8_t const *, int, int);
__extern int pipi_close_sequence(pipi_sequence_t *);

#ifdef __cplusplus
}
#endif

#endif /* __PIPI_H__ */


+ 285
- 0
src/image/pixels.cpp Voir le fichier

@@ -0,0 +1,285 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* pixels.c: pixel-level image manipulation
*/

#include "config.h"

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

#include <math.h>

#include "pipi.h"
#include "pipi-internals.h"

static void init_tables(void);

static double global_gamma = 2.2;
static int done = 0;

static float u8tof32_table[256];
static inline float u8tof32(uint8_t p) { return u8tof32_table[(int)p]; }

/* Return a direct pointer to an image's pixels. */
pipi_pixels_t *pipi_get_pixels(pipi_image_t *img, pipi_format_t type)
{
size_t bytes = 0;
int x, y, i, bpp = 0;

if(type < 0 || type >= PIPI_PIXELS_MAX)
return NULL;

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

/* Handle special cases */
if(type == PIPI_PIXELS_MASK_U8)
return &img->p[type];

/* Preliminary conversions */
if(img->last_modified == PIPI_PIXELS_RGBA_U8
&& type == PIPI_PIXELS_Y_F32)
pipi_get_pixels(img, PIPI_PIXELS_RGBA_F32);
else if(img->last_modified == PIPI_PIXELS_BGR_U8
&& type == PIPI_PIXELS_Y_F32)
pipi_get_pixels(img, PIPI_PIXELS_RGBA_F32);
else if(img->last_modified == PIPI_PIXELS_Y_F32
&& type == PIPI_PIXELS_RGBA_U8)
pipi_get_pixels(img, PIPI_PIXELS_RGBA_F32);
else if(img->last_modified == PIPI_PIXELS_Y_F32
&& type == PIPI_PIXELS_BGR_U8)
pipi_get_pixels(img, PIPI_PIXELS_RGBA_F32);

/* Allocate pixels if necessary */
if(!img->p[type].pixels)
{
switch(type)
{
case PIPI_PIXELS_RGBA_U8:
bytes = img->w * img->h * 4 * sizeof(uint8_t);
bpp = 4 * sizeof(uint8_t);
break;
case PIPI_PIXELS_BGR_U8:
bytes = img->w * img->h * 3 * sizeof(uint8_t);
bpp = 3 * sizeof(uint8_t);
break;
case PIPI_PIXELS_RGBA_F32:
bytes = img->w * img->h * 4 * sizeof(float);
bpp = 4 * sizeof(float);
break;
case PIPI_PIXELS_Y_F32:
bytes = img->w * img->h * sizeof(float);
bpp = sizeof(float);
break;
default:
return NULL;
}

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

/* Convert pixels */
if(img->last_modified == PIPI_PIXELS_RGBA_U8
&& type == PIPI_PIXELS_RGBA_F32)
{
uint8_t *src = (uint8_t *)img->p[PIPI_PIXELS_RGBA_U8].pixels;
float *dest = (float *)img->p[type].pixels;

init_tables();

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]
= u8tof32(src[4 * (y * img->w + x) + i]);
}
else if(img->last_modified == PIPI_PIXELS_BGR_U8
&& type == PIPI_PIXELS_RGBA_F32)
{
uint8_t *src = (uint8_t *)img->p[PIPI_PIXELS_BGR_U8].pixels;
float *dest = (float *)img->p[type].pixels;

init_tables();

for(y = 0; y < img->h; y++)
for(x = 0; x < img->w; x++)
{
dest[4 * (y * img->w + x)]
= u8tof32(src[3 * (y * img->w + x) + 2]);
dest[4 * (y * img->w + x) + 1]
= u8tof32(src[3 * (y * img->w + x) + 1]);
dest[4 * (y * img->w + x) + 2]
= u8tof32(src[3 * (y * img->w + x)]);
dest[4 * (y * img->w + x) + 3] = 1.0;
}
}
else if(img->last_modified == PIPI_PIXELS_RGBA_F32
&& type == PIPI_PIXELS_RGBA_U8)
{
float *src = (float *)img->p[PIPI_PIXELS_RGBA_F32].pixels;
uint8_t *dest = (uint8_t *)img->p[type].pixels;

init_tables();

for(y = 0; y < img->h; y++)
for(x = 0; x < img->w; x++)
for(i = 0; i < 4; i++)
{
double p, e;
uint8_t d;

p = src[4 * (y * img->w + x) + i];

if(p < 0.) d = 0.;
else if(p > 1.) d = 255;
else d = (int)(255.999 * pow(p, 1. / global_gamma));

dest[4 * (y * img->w + x) + i] = d;

e = (p - u8tof32(d)) / 16;
if(x < img->w - 1)
src[4 * (y * img->w + x + 1) + i] += e * 7;
if(y < img->h - 1)
{
if(x > 0)
src[4 * ((y + 1) * img->w + x - 1) + i] += e * 3;
src[4 * ((y + 1) * img->w + x) + i] += e * 5;
if(x < img->w - 1)
src[4 * ((y + 1) * img->w + x + 1) + i] += e;
}
}
}
else if(img->last_modified == PIPI_PIXELS_RGBA_F32
&& type == PIPI_PIXELS_BGR_U8)
{
float *src = (float *)img->p[PIPI_PIXELS_RGBA_F32].pixels;
uint8_t *dest = (uint8_t *)img->p[type].pixels;

init_tables();

for(y = 0; y < img->h; y++)
for(x = 0; x < img->w; x++)
for(i = 0; i < 3; i++)
{
double p, e;
uint8_t d;

p = src[4 * (y * img->w + x) + i];

if(p < 0.) d = 0.;
else if(p > 1.) d = 255;
else d = (int)(255.999 * pow(p, 1. / global_gamma));

dest[3 * (y * img->w + x) + i] = d;

e = (p - u8tof32(d)) / 16;
if(x < img->w - 1)
src[4 * (y * img->w + x + 1) + i] += e * 7;
if(y < img->h - 1)
{
if(x > 0)
src[4 * ((y + 1) * img->w + x - 1) + i] += e * 3;
src[4 * ((y + 1) * img->w + x) + i] += e * 5;
if(x < img->w - 1)
src[4 * ((y + 1) * img->w + x + 1) + i] += e;
}
}
}
else if(img->last_modified == PIPI_PIXELS_Y_F32
&& type == PIPI_PIXELS_RGBA_F32)
{
float *src = (float *)img->p[PIPI_PIXELS_Y_F32].pixels;
float *dest = (float *)img->p[PIPI_PIXELS_RGBA_F32].pixels;

init_tables();

for(y = 0; y < img->h; y++)
for(x = 0; x < img->w; x++)
{
float p = src[y * img->w + x];
dest[4 * (y * img->w + x)] = p;
dest[4 * (y * img->w + x) + 1] = p;
dest[4 * (y * img->w + x) + 2] = p;
dest[4 * (y * img->w + x) + 3] = 1.0;
}
}
else if(img->last_modified == PIPI_PIXELS_RGBA_F32
&& type == PIPI_PIXELS_Y_F32)
{
float *src = (float *)img->p[PIPI_PIXELS_RGBA_F32].pixels;
float *dest = (float *)img->p[PIPI_PIXELS_Y_F32].pixels;

init_tables();

for(y = 0; y < img->h; y++)
for(x = 0; x < img->w; x++)
{
float p = 0.;
p += 0.299 * src[4 * (y * img->w + x)];
p += 0.587 * src[4 * (y * img->w + x) + 1];
p += 0.114 * src[4 * (y * img->w + x) + 2];
dest[y * img->w + x] = p;
}
}
else
{
memset(img->p[type].pixels, 0, bytes);
}

img->last_modified = type;

return &img->p[type];
}

void pipi_release_pixels(pipi_image_t *img, pipi_pixels_t *p)
{
return;
}

void pipi_set_colorspace(pipi_image_t *img, pipi_format_t fmt)
{
pipi_pixels_t *p = pipi_get_pixels(img, fmt);
pipi_release_pixels(img, p);
}

void pipi_set_gamma(double g)
{
if(g > 0.)
{
global_gamma = g;
done = 0;
}
}

static void init_tables(void)
{
int i;

if(done)
return;

for(i = 0; i < 256; i++)
u8tof32_table[i] = pow((double)i / 255., global_gamma);

done = 1;
}


+ 509
- 0
src/image/quantize/reduce.cpp Voir le fichier

@@ -0,0 +1,509 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* reduce.c: palette reduction routines
*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

#define R 0
#define G 1
#define B 2
#define X 3
#define Y 4
#define A 5

#define BRIGHT(x) (0.299*(x)[0] + 0.587*(x)[1] + 0.114*(x)[2])

#define MAXCOLORS 16
#define STEPS 1024
#define EPSILON (0.000001)

typedef struct
{
double pts[STEPS + 1][MAXCOLORS * (MAXCOLORS - 1) / 2][6];
int hullsize[STEPS + 1];
double bary[STEPS + 1][3];
}
hull_t;

static double const y[3] = { .299, .587, .114 };
static double u[3], v[3];
static int ylen;

/*
* Find two base vectors for the chrominance planes.
*/
static void init_uv(void)
{
double tmp;

ylen = sqrt(y[R] * y[R] + y[G] * y[G] + y[B] * y[B]);

u[R] = y[1];
u[G] = -y[0];
u[B] = 0;
tmp = sqrt(u[R] * u[R] + u[G] * u[G] + u[B] * u[B]);
u[R] /= tmp; u[G] /= tmp; u[B] /= tmp;

v[R] = y[G] * u[B] - y[B] * u[G];
v[G] = y[B] * u[R] - y[R] * u[B];
v[B] = y[R] * u[G] - y[G] * u[R];
tmp = sqrt(v[R] * v[R] + v[G] * v[G] + v[B] * v[B]);
v[R] /= tmp; v[G] /= tmp; v[B] /= tmp;
}

/*
* Compute the convex hull of a given palette.
*/
static hull_t *compute_hull(int ncolors, double const *palette)
{
double pal[MAXCOLORS][3];
double gray[3];
double *dark = NULL, *light = NULL;
double tmp, min = 1.0, max = 0.0;
hull_t *ret = malloc(sizeof(hull_t));
int i, j, n;

debug("");
debug("### NEW HULL ###");
debug("");

debug("Analysing %i colors", ncolors);

for(i = 0; i < ncolors; i++)
{
pal[i][R] = palette[i * 3];
pal[i][G] = palette[i * 3 + 1];
pal[i][B] = palette[i * 3 + 2];
debug(" [%i] (%g,%g,%g)", i, pal[i][R], pal[i][G], pal[i][B]);
}

/*
* 1. Find the darkest and lightest colours
*/
for(i = 0; i < ncolors; i++)
{
double p = BRIGHT(pal[i]);
if(p < min)
{
dark = pal[i];
min = p;
}
if(p > max)
{
light = pal[i];
max = p;
}
}

gray[R] = light[R] - dark[R];
gray[G] = light[G] - dark[G];
gray[B] = light[B] - dark[B];

debug(" gray axis (%g,%g,%g) - (%g,%g,%g)",
dark[R], dark[G], dark[B], light[R], light[G], light[B]);

/*
* 3. Browse the grey axis and do stuff
*/
for(n = 0; n <= STEPS; n++)
{
double pts[MAXCOLORS * (MAXCOLORS - 1) / 2][5];
double ptmp[5];
double p0[3];
#define SWAP(p1,p2) do { memcpy(ptmp, p1, sizeof(ptmp)); \
memcpy(p1, p2, sizeof(ptmp)); \
memcpy(p2, ptmp, sizeof(ptmp)); } while(0)
double ctx, cty, weight;
double t = n * 1.0 / STEPS;
int npts = 0, left;

debug("Slice %i/%i", n, STEPS);

p0[R] = dark[R] + t * gray[R];
p0[G] = dark[G] + t * gray[G];
p0[B] = dark[B] + t * gray[B];

debug(" 3D gray (%g,%g,%g)", p0[R], p0[G], p0[B]);

/*
* 3.1. Find all edges that intersect the t.y + (u,v) plane
*/
for(i = 0; i < ncolors; i++)
{
double k1[3];
double yk1;
k1[R] = pal[i][R] - p0[R];
k1[G] = pal[i][G] - p0[G];
k1[B] = pal[i][B] - p0[B];
tmp = sqrt(k1[R] * k1[R] + k1[G] * k1[G] + k1[B] * k1[B]);

/* If k1.y > t.y.y, we don't want this point */
yk1 = y[R] * k1[R] + y[G] * k1[G] + y[B] * k1[B];
if(yk1 > t * ylen * ylen + EPSILON)
continue;

for(j = 0; j < ncolors; j++)
{
double k2[3];
double yk2, s;

if(i == j)
continue;

k2[R] = pal[j][R] - p0[R];
k2[G] = pal[j][G] - p0[G];
k2[B] = pal[j][B] - p0[B];
tmp = sqrt(k2[R] * k2[R] + k2[G] * k2[G] + k2[B] * k2[B]);

/* If k2.y < t.y.y, we don't want this point */
yk2 = y[R] * k2[R] + y[G] * k2[G] + y[B] * k2[B];
if(yk2 < t * ylen * ylen - EPSILON)
continue;

if(yk2 < yk1)
continue;

s = yk1 == yk2 ? 0.5 : (t * ylen * ylen - yk1) / (yk2 - yk1);

pts[npts][R] = p0[R] + k1[R] + s * (k2[R] - k1[R]);
pts[npts][G] = p0[G] + k1[G] + s * (k2[G] - k1[G]);
pts[npts][B] = p0[B] + k1[B] + s * (k2[B] - k1[B]);
npts++;
}
}

/*
* 3.2. Find the barycentre of these points' convex hull. We use
* the Graham Scan technique.
*/

/* Make our problem a 2-D problem. */
for(i = 0; i < npts; i++)
{
pts[i][X] = (pts[i][R] - p0[R]) * u[R]
+ (pts[i][G] - p0[G]) * u[G]
+ (pts[i][B] - p0[B]) * u[B];
pts[i][Y] = (pts[i][R] - p0[R]) * v[R]
+ (pts[i][G] - p0[G]) * v[G]
+ (pts[i][B] - p0[B]) * v[B];
}

/* Find the leftmost point */
left = -1;
tmp = 10.;
for(i = 0; i < npts; i++)
if(pts[i][X] < tmp)
{
left = i;
tmp = pts[i][X];
}
SWAP(pts[0], pts[left]);

/* Sort the remaining points radially around pts[0]. Bubble sort
* is okay for small sizes, I don't care. */
for(i = 1; i < npts; i++)
for(j = 1; j < npts - i; j++)
{
double k1 = (pts[j][X] - pts[0][X])
* (pts[j + 1][Y] - pts[0][Y]);
double k2 = (pts[j + 1][X] - pts[0][X])
* (pts[j][Y] - pts[0][Y]);
if(k1 < k2 - EPSILON)
SWAP(pts[j], pts[j + 1]);
else if(k1 < k2 + EPSILON)
{
/* Aligned! keep the farthest point */
double ax = pts[j][X] - pts[0][X];
double ay = pts[j][Y] - pts[0][Y];
double bx = pts[j + 1][X] - pts[0][X];
double by = pts[j + 1][Y] - pts[0][Y];

if(ax * ax + ay * ay > bx * bx + by * by)
SWAP(pts[j], pts[j + 1]);
}
}

/* Remove points not in the convex hull */
for(i = 2; i < npts; /* */)
{
double k1, k2;

if(i < 2)
{
i++;
continue;
}

k1 = (pts[i - 1][X] - pts[i - 2][X])
* (pts[i][Y] - pts[i - 2][Y]);
k2 = (pts[i][X] - pts[i - 2][X])
* (pts[i - 1][Y] - pts[i - 2][Y]);
if(k1 <= k2 + EPSILON)
{
for(j = i - 1; j < npts - 1; j++)
SWAP(pts[j], pts[j + 1]);
npts--;
}
else
i++;
}
/* FIXME: check the last point */

for(i = 0; i < npts; i++)
debug(" 2D pt[%i] (%g,%g)", i, pts[i][X], pts[i][Y]);

/* Compute the barycentre coordinates */
ctx = 0.;
cty = 0.;
weight = 0.;
for(i = 2; i < npts; i++)
{
double abx = pts[i - 1][X] - pts[0][X];
double aby = pts[i - 1][Y] - pts[0][Y];
double acx = pts[i][X] - pts[0][X];
double acy = pts[i][Y] - pts[0][Y];
double area;
double sqarea = (abx * abx + aby * aby) * (acx * acx + acy * acy)
- (abx * acx + aby * acy) * (abx * acx + aby * acy);
if(sqarea <= 0.)
continue;

area = sqrt(sqarea);
ctx += area * (abx + acx) / 3;
cty += area * (aby + acy) / 3;
weight += area;
}

if(weight > EPSILON)
{
ctx = pts[0][X] + ctx / weight;
cty = pts[0][Y] + cty / weight;
}
else
{
int right = -1;
tmp = -10.;
for(i = 0; i < npts; i++)
if(pts[i][X] > tmp)
{
right = i;
tmp = pts[i][X];
}
ctx = 0.5 * (pts[0][X] + pts[right][X]);
cty = 0.5 * (pts[0][Y] + pts[right][Y]);
}

debug(" 2D bary (%g,%g)", ctx, cty);

/*
* 3.3. Store the barycentre and convex hull information.
*/

ret->bary[n][R] = p0[R] + ctx * u[R] + cty * v[R];
ret->bary[n][G] = p0[G] + ctx * u[G] + cty * v[G];
ret->bary[n][B] = p0[B] + ctx * u[B] + cty * v[B];

for(i = 0; i < npts; i++)
{
ret->pts[n][i][R] = pts[i][R];
ret->pts[n][i][G] = pts[i][G];
ret->pts[n][i][B] = pts[i][B];
ret->pts[n][i][X] = pts[i][X] - ctx;
ret->pts[n][i][Y] = pts[i][Y] - cty;
ret->pts[n][i][A] = atan2(pts[i][Y] - cty, pts[i][X] - ctx);

debug(" 3D pt[%i] (%g,%g,%g) angle %g",
i, pts[i][R], pts[i][G], pts[i][B], ret->pts[n][i][A]);
}
ret->hullsize[n] = npts;

debug(" 3D bary (%g,%g,%g)",
ret->bary[n][R], ret->bary[n][G], ret->bary[n][B]);
}

return ret;
}


pipi_image_t *pipi_reduce(pipi_image_t *src,
int ncolors, double const *palette)
{
static double const rgbpal[] =
{
0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1,
1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1,
};

pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
float *srcdata, *dstdata;
hull_t *rgbhull, *myhull;
int i, j, w, h;

init_uv();

rgbhull = compute_hull(8, rgbpal);
myhull = compute_hull(ncolors, palette);

/*
* 4. Load image and change its palette.
*/

debug("");
debug("### PROCESSING IMAGE ###");
debug("");

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

w = srcp->w;
h = srcp->h;

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

for(j = 0; j < h; j++)
for(i = 0; i < w; i++)
{
double p[3];
double xp, yp, angle, xa, ya, xb, yb, t, s;
int slice, n, count;

/* FIXME: Imlib fucks up the RGB order. */
p[B] = srcdata[4 * (j * w + i)];
p[G] = srcdata[4 * (j * w + i) + 1];
p[R] = srcdata[4 * (j * w + i) + 2];

debug("Pixel +%i+%i (%g,%g,%g)", i, j, p[R], p[G], p[B]);

slice = (int)(BRIGHT(p) * STEPS + 0.5);

debug(" slice %i", slice);

/* Convert to 2D. The origin is the slice's barycentre. */
xp = (p[R] - rgbhull->bary[slice][R]) * u[R]
+ (p[G] - rgbhull->bary[slice][G]) * u[G]
+ (p[B] - rgbhull->bary[slice][B]) * u[B];
yp = (p[R] - rgbhull->bary[slice][R]) * v[R]
+ (p[G] - rgbhull->bary[slice][G]) * v[G]
+ (p[B] - rgbhull->bary[slice][B]) * v[B];

debug(" 2D pt (%g,%g)", xp, yp);

/* 1. find the excentricity in RGB space. There is an easier
* way to do this, which is to find the intersection of our
* line with the RGB cube itself, but we'd lose the possibility
* of having an original colour space other than RGB. */

/* First, find the relevant triangle. */
count = rgbhull->hullsize[slice];
angle = atan2(yp, xp);
for(n = 0; n < count; n++)
{
double a1 = rgbhull->pts[slice][n][A];
double a2 = rgbhull->pts[slice][(n + 1) % count][A];
if(a1 > a2)
{
if(angle >= a1)
a2 += 2 * M_PI;
else
a1 -= 2 * M_PI;
}
if(angle >= a1 && angle <= a2)
break;
}

/* Now compute the distance to the triangle's edge. If the edge
* intersection is M, then t is such as P = t.M (can be zero) */
xa = rgbhull->pts[slice][n % count][X];
ya = rgbhull->pts[slice][n % count][Y];
xb = rgbhull->pts[slice][(n + 1) % count][X];
yb = rgbhull->pts[slice][(n + 1) % count][Y];
t = (xp * (yb - ya) - yp * (xb - xa)) / (xa * yb - xb * ya);

if(t > 1.0)
t = 1.0;

debug(" best RGB %g (%g,%g) (%g,%g)", t, xa, ya, xb, yb);

/* 2. apply the excentricity in reduced space. */

count = myhull->hullsize[slice];
for(n = 0; n < count; n++)
{
double a1 = myhull->pts[slice][n][A];
double a2 = myhull->pts[slice][(n + 1) % count][A];
if(a1 > a2)
{
if(angle >= a1)
a2 += 2 * M_PI;
else
a1 -= 2 * M_PI;
}
if(angle >= a1 && angle <= a2)
break;
}

/* If the edge intersection is M', s is such as P = s.M'. We
* want P' = t.M' = t.P/s */
xa = myhull->pts[slice][n % count][X];
ya = myhull->pts[slice][n % count][Y];
xb = myhull->pts[slice][(n + 1) % count][X];
yb = myhull->pts[slice][(n + 1) % count][Y];
s = (xp * (yb - ya) - yp * (xb - xa)) / (xa * yb - xb * ya);

debug(" best custom %g (%g,%g) (%g,%g)", s, xa, ya, xb, yb);

if(s > 0)
{
xp *= t / s;
yp *= t / s;
}

p[R] = myhull->bary[slice][R] + xp * u[R] + yp * v[R];
p[G] = myhull->bary[slice][G] + xp * u[G] + yp * v[G];
p[B] = myhull->bary[slice][B] + xp * u[B] + yp * v[B];

/* Clipping should not be necessary, but the above code
* is unfortunately not perfect. */
if(p[R] < 0.0) p[R] = 0.0; else if(p[R] > 1.0) p[R] = 1.0;
if(p[G] < 0.0) p[G] = 0.0; else if(p[G] > 1.0) p[G] = 1.0;
if(p[B] < 0.0) p[B] = 0.0; else if(p[B] > 1.0) p[B] = 1.0;

dstdata[4 * (j * w + i)] = p[B];
dstdata[4 * (j * w + i) + 1] = p[G];
dstdata[4 * (j * w + i) + 2] = p[R];
}

free(rgbhull);
free(myhull);

return dst;
}


+ 61
- 0
src/image/render/noise.cpp Voir le fichier

@@ -0,0 +1,61 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* noise.c: noise rendering functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_render_random(int w, int h)
{
pipi_image_t *ret;
pipi_pixels_t *pix;
float *data;
unsigned int ctx = 1;
int x, y, t;

ret = pipi_new(w, h);
pix = pipi_get_pixels(ret, PIPI_PIXELS_RGBA_F32);
data = (float *)pix->pixels;

for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
{
for(t = 0; t < 3; t++)
{
long hi, lo;

hi = ctx / 12773L;
lo = ctx % 12773L;
ctx = 16807L * lo - 2836L * hi;
if(ctx <= 0)
ctx += 0x7fffffffL;

data[4 * (y * w + x) + t]
= (double)((ctx % 65536) / 65535.);
}
data[4 * (y * w + x) + 3] = 1.0;
}

return ret;
}


+ 128
- 0
src/image/render/screen.cpp Voir le fichier

@@ -0,0 +1,128 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* screen.c: halftoning screen functions
*/

#include "config.h"

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

#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_render_bayer(int w, int h)
{
pipi_image_t *ret;
pipi_pixels_t *pix;
float *data;
int i, j, n;

if(w <= 0 || h <= 0)
return NULL;

for(n = 1; n < w || n < h; n *= 2)
;

ret = pipi_new(w, h);
pix = pipi_get_pixels(ret, PIPI_PIXELS_Y_F32);
data = (float *)pix->pixels;

for(j = 0; j < h; j++)
for(i = 0; i < w; i++)
{
int k, l, x = 0;

for(k = 1, l = n * n / 4; k < n; k *= 2, l /= 4)
{
if((i & k) && (j & k))
x += l;
else if(i & k)
x += 3 * l;
else if(j & k)
x += 2 * l;
}

data[j * w + i] = (double)(x + 1) / (n * n + 1);
}

return ret;
}

typedef struct
{
int x, y;
double dist;
}
dot_t;

static int cmpdot(const void *p1, const void *p2)
{
return ((dot_t const *)p1)->dist > ((dot_t const *)p2)->dist;
}

pipi_image_t *pipi_render_halftone(int w, int h)
{
pipi_image_t *ret;
pipi_pixels_t *pix;
float *data;
dot_t *circle;
int x, y, n;

if(w <= 0 || h <= 0)
return NULL;

circle = malloc(w * h * sizeof(dot_t));

for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
{
double dy = ((double)y + .07) / h - .5;
double dx = (double)x / w - .5;
/* Using dx²+dy² here creates another interesting halftone. */
double r = - cos(M_PI * (dx - dy)) - cos(M_PI * (dx + dy));
circle[y * w + x].x = x;
circle[y * w + x].y = y;
circle[y * w + x].dist = r;
}
qsort(circle, w * h, sizeof(dot_t), cmpdot);

ret = pipi_new(w * 2, h * 2);
pix = pipi_get_pixels(ret, PIPI_PIXELS_Y_F32);
data = (float *)pix->pixels;

for(n = 0; n < w * h; n++)
{
x = circle[n].x;
y = circle[n].y;

data[y * (2 * w) + x] = (float)(2 * n + 1) / (w * h * 4 + 1);
data[(y + h) * (2 * w) + x + w] = (float)(2 * n + 2) / (w * h * 4 + 1);
data[(y + h) * (2 * w) + x] = 1. - (float)(2 * n + 1) / (w * h * 4 + 1);
data[y * (2 * w) + x + w] = 1. - (float)(2 * n + 2) / (w * h * 4 + 1);
}

free(circle);

return ret;
}


+ 136
- 0
src/image/resample/bicubic.cpp Voir le fichier

@@ -0,0 +1,136 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2010 Sam Hocevar <sam@hocevar.net>
* 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.
*/

/*
* bicubic.c: Bicubic image resizing functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

static inline int min_int(int a, int b) { return a < b ? a : b; }
static inline int max_int(int a, int b) { return a > b ? a : b; }

pipi_image_t *pipi_resize_bicubic(pipi_image_t *src, int w, int h)
{
float *srcdata, *dstdata, *p0, *p1, *p2, *p3;
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
int x, y, i, sw, dw, sh, dh, i0, i1, i2, i3;
float scalex, scaley;

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

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

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

scalex = dw > 1 ? (float)(sw - 1) / (dw - 1) : 1.0f;
scaley = dh > 1 ? (float)(sh - 1) / (dh - 1) : 1.0f;

for(y = 0; y < dh; y++)
{
float yfloat = scaley * y;
int yint = (int)yfloat;
float y1 = yfloat - yint;

p0 = srcdata + 4 * sw * min_int(max_int(0, yint - 1), sh - 1);
p1 = srcdata + 4 * sw * min_int(max_int(0, yint ), sh - 1);
p2 = srcdata + 4 * sw * min_int(max_int(0, yint + 1), sh - 1);
p3 = srcdata + 4 * sw * min_int(max_int(0, yint + 2), sh - 1);

for (x = 0; x < dw; x++)
{
float xfloat = scalex * x;
int xint = (int)xfloat;
float x1 = xfloat - xint;

i0 = 4 * min_int(max_int(0, xint - 1), sw - 1);
i1 = 4 * min_int(max_int(0, xint ), sw - 1);
i2 = 4 * min_int(max_int(0, xint + 1), sw - 1);
i3 = 4 * min_int(max_int(0, xint + 2), sw - 1);

for (i = 0; i < 4; i++, i0++, i1++, i2++, i3++)
{
float a00 = p1[i1];
float a01 = .5f * (p2[i1] - p0[i1]);
float a02 = p0[i1] - 2.5f * p1[i1]
+ 2.f * p2[i1] - .5f * p3[i1];
float a03 = .5f * (p3[i1] - p0[i1]) + 1.5f * (p1[i1] - p2[i1]);

float a10 = .5f * (p1[i2] - p1[i0]);
float a11 = .25f * (p0[i0] - p2[i0] - p0[i2] + p2[i2]);
float a12 = .5f * (p0[i2] - p0[i0]) + 1.25f * (p1[i0] - p1[i2])
+ .25f * (p3[i0] - p3[i2]) + p2[i2] - p2[i0];
float a13 = .25f * (p0[i0] - p3[i0] - p0[i2] + p3[i2])
+ .75f * (p2[i0] - p1[i0] + p1[i2] - p2[i2]);

float a20 = p1[i0] - 2.5f * p1[i1]
+ 2.f * p1[i2] - .5f * p1[i3];
float a21 = .5f * (p2[i0] - p0[i0]) + 1.25f * (p0[i1] - p2[i1])
+ .25f * (p0[i3] - p2[i3]) - p0[i2] + p2[i2];
float a22 = p0[i0] - p3[i2] - 2.5f * (p1[i0] + p0[i1])
+ 2.f * (p2[i0] + p0[i2]) - .5f * (p3[i0] + p0[i3])
+ 6.25f * p1[i1] - 5.f * (p2[i1] + p1[i2])
+ 1.25f * (p3[i1] + p1[i3])
+ 4.f * p2[i2] - p2[i3] + .25f * p3[i3];
float a23 = 1.5f * (p1[i0] - p2[i0]) + .5f * (p3[i0] - p0[i0])
+ 1.25f * (p0[i1] - p3[i1])
+ 3.75f * (p2[i1] - p1[i1]) + p3[i2] - p0[i2]
+ 3.f * (p1[i2] - p2[i2]) + .25f * (p0[i3] - p3[i3])
+ .75f * (p2[i3] - p1[i3]);

float a30 = .5f * (p1[i3] - p1[i0]) + 1.5f * (p1[i1] - p1[i2]);
float a31 = .25f * (p0[i0] - p2[i0]) + .25f * (p2[i3] - p0[i3])
+ .75f * (p2[i1] - p0[i1] + p0[i2] - p2[i2]);
float a32 = -.5f * p0[i0] + 1.25f * p1[i0] - p2[i0]
+ .25f * p3[i0] + 1.5f * p0[i1] - 3.75f * p1[i1]
+ 3.f * p2[i1] - .75f * p3[i1] - 1.5f * p0[i2]
+ 3.75f * p1[i2] - 3.f * p2[i2] + .75f * p3[i2]
+ .5f * p0[i3] - 1.25f * p1[i3] + p2[i3]
- .25f * p3[i3];
float a33 = .25f * p0[i0] - .75f * p1[i0] + .75f * p2[i0]
- .25f * p3[i0] - .75f * p0[i1] + 2.25f * p1[i1]
- 2.25f * p2[i1] + .75f * p3[i1] + .75f * p0[i2]
- 2.25f * p1[i2] + 2.25f * p2[i2] - .75f * p3[i2]
- .25f * p0[i3] + .75f * p1[i3] - .75f * p2[i3]
+ .25f * p3[i3];

float y2 = y1 * y1; float y3 = y2 * y1;
float x2 = x1 * x1; float x3 = x2 * x1;

float p = a00 + a01 * y1 + a02 * y2 + a03 * y3 +
+ a10 * x1 + a11 * x1 * y1 + a12 * x1 * y2 + a13 * x1 * y3
+ a20 * x2 + a21 * x2 * y1 + a22 * x2 * y2 + a23 * x2 * y3
+ a30 * x3 + a31 * x3 * y1 + a32 * x3 * y2 + a33 * x3 * y3;
if (p < 0.0f) p = 0.0f;
if (p > 1.0f) p = 1.0f;

dstdata[(y * dw + x) * 4 + i] = p;
}
}
}

return dst;
}


+ 131
- 0
src/image/resample/bresenham.cpp Voir le fichier

@@ -0,0 +1,131 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2009 Sam Hocevar <sam@hocevar.net>
* 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.
*/

/*
* bresenham.c: Bresenham image resizing functions
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

/* This is Bresenham resizing. I rediscovered it independently but it was
* actually first described in 1995 by Tim Kientzle in "Scaling Bitmaps
* with Bresenham". */

/* FIXME: the algorithm does not handle alpha components properly. Resulting
* alpha should be the mean alpha value of the neightbouring pixels, but
* the colour components should be weighted with the alpha value. */
pipi_image_t *pipi_resize_bresenham(pipi_image_t *src, int w, int h)
{
float *srcdata, *dstdata, *aline, *line;
pipi_image_t *dst;
pipi_pixels_t *srcp, *dstp;
int x, y, x0, y0, sw, dw, sh, dh, remy;
float invswsh;

srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32);
srcdata = (float *)srcp->pixels;

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

sw = src->w; sh = src->h;
dw = dst->w; dh = dst->h;
invswsh = 1.0f / (sw * sh);

aline = malloc(4 * dw * sizeof(float));
line = malloc(4 * dw * sizeof(float));

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

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

memset(aline, 0, 4 * dw * sizeof(float));

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

for(x = 0, x0 = 0; x < dw; x++)
{
float ar = 0, ag = 0, ab = 0, aa = 0;
int totx = 0, nx;

while(totx < sw)
{
if(remx == 0)
{
r = srcdata[(y0 * sw + x0) * 4];
g = srcdata[(y0 * sw + x0) * 4 + 1];
b = srcdata[(y0 * sw + x0) * 4 + 2];
a = srcdata[(y0 * sw + x0) * 4 + 3];
x0++;
remx = dw;
}

nx = (totx + remx <= sw) ? remx : sw - totx;
ar += nx * r; ag += nx * g; ab += nx * b; aa += nx * a;
totx += nx;
remx -= nx;
}

line[4 * x] = ar;
line[4 * x + 1] = ag;
line[4 * x + 2] = ab;
line[4 * x + 3] = aa;
}

y0++;
remy = dh;
}

ny = (toty + remy <= sh) ? remy : sh - toty;
for(x = 0; x < dw; x++)
{
aline[4 * x] += ny * line[4 * x];
aline[4 * x + 1] += ny * line[4 * x + 1];
aline[4 * x + 2] += ny * line[4 * x + 2];
aline[4 * x + 3] += ny * line[4 * x + 3];
}
toty += ny;
remy -= ny;
}

for(x = 0; x < dw; x++)
{
dstdata[(y * dw + x) * 4] = aline[4 * x] * invswsh;
dstdata[(y * dw + x) * 4 + 1] = aline[4 * x + 1] * invswsh;
dstdata[(y * dw + x) * 4 + 2] = aline[4 * x + 2] * invswsh;
dstdata[(y * dw + x) * 4 + 3] = aline[4 * x + 3] * invswsh;
}
}

free(aline);
free(line);

return dst;
}


+ 346
- 0
src/image/sequence.cpp Voir le fichier

@@ -0,0 +1,346 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2009 Sam Hocevar <sam@hocevar.net>
* 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.
*/

/*
* codec.c: image I/O functions
*/

#include "config.h"

#if defined _WIN32
# undef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS /* I know how to use snprintf, thank you */
# define snprintf _snprintf
#endif

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

#if defined USE_FFMPEG
# include <libavformat/avformat.h>
# include <libswscale/swscale.h>
#endif

#include "pipi.h"
#include "pipi-internals.h"

#if defined USE_FFMPEG
typedef struct
{
uint8_t *buf;
size_t buf_len;

AVFormatContext *fmt_ctx;
AVStream *stream;
AVCodecContext *cod_ctx;
AVCodec *codec;
AVFrame *frame;

struct SwsContext *sws_ctx;
int src_width, src_height, src_fmt;
}
ffmpeg_codec_t;
#endif

pipi_sequence_t *pipi_open_sequence(char const *file,
int width, int height, int rgb, int fps,
int par_num, int par_den, int bitrate)
{
#if defined USE_FFMPEG
static int initialised = 0;

pipi_sequence_t *seq;
ffmpeg_codec_t *ff;
uint8_t *tmp;

seq = malloc(sizeof(pipi_sequence_t));
seq->w = width;
seq->h = height;
seq->fps = fps;
seq->convert_buf = NULL;

ff = malloc(sizeof(ffmpeg_codec_t));
memset(ff, 0, sizeof(*ff));

seq->codec_priv = ff;

if (!initialised)
{
av_register_all();
initialised = 1;
}

ff->fmt_ctx = avformat_alloc_context();
if (!ff->fmt_ctx)
goto error;

/* Careful here: the Win32 snprintf doesn't seem to add a trailing
* zero to the truncated output. */
snprintf(ff->fmt_ctx->filename, sizeof(ff->fmt_ctx->filename),
file);
ff->fmt_ctx->filename[sizeof(ff->fmt_ctx->filename) - 1] = '\0';

ff->fmt_ctx->oformat = av_guess_format(NULL, file, NULL);
if (!ff->fmt_ctx->oformat)
ff->fmt_ctx->oformat = av_guess_format("mpeg", NULL, NULL);
if (!ff->fmt_ctx->oformat)
goto error;

ff->stream = av_new_stream(ff->fmt_ctx, 0);
if (!ff->stream)
goto error;

ff->stream->sample_aspect_ratio.num = par_num;
ff->stream->sample_aspect_ratio.den = par_den;

ff->cod_ctx = ff->stream->codec;

ff->cod_ctx->width = width;
ff->cod_ctx->height = height;
ff->cod_ctx->sample_aspect_ratio.num = par_num;
ff->cod_ctx->sample_aspect_ratio.den = par_den;
ff->cod_ctx->codec_id = ff->fmt_ctx->oformat->video_codec;
ff->cod_ctx->codec_type = CODEC_TYPE_VIDEO;
ff->cod_ctx->bit_rate = bitrate;
ff->cod_ctx->time_base.num = 1;
ff->cod_ctx->time_base.den = fps;

ff->cod_ctx->pix_fmt = PIX_FMT_YUV420P; /* send YUV 420 */
if (ff->cod_ctx->codec_id == CODEC_ID_MPEG2VIDEO)
ff->cod_ctx->max_b_frames = 2;
if (ff->cod_ctx->codec_id == CODEC_ID_MPEG1VIDEO)
ff->cod_ctx->mb_decision = 2;
if (ff->cod_ctx->codec_id == CODEC_ID_H264)
{
/* Import x264 slow presets */
ff->cod_ctx->coder_type = 1;
ff->cod_ctx->flags |= CODEC_FLAG_LOOP_FILTER;
ff->cod_ctx->me_cmp |= FF_CMP_CHROMA;
ff->cod_ctx->partitions |= X264_PART_I4X4 | X264_PART_I8X8
| X264_PART_P4X4 | X264_PART_P8X8;
ff->cod_ctx->me_method = ME_UMH;
ff->cod_ctx->me_subpel_quality = 8;
ff->cod_ctx->me_range = 16;
ff->cod_ctx->gop_size = 250;
ff->cod_ctx->keyint_min = 25;
ff->cod_ctx->scenechange_threshold = 40;
ff->cod_ctx->i_quant_factor = 0.71f;
ff->cod_ctx->b_frame_strategy = 2;
ff->cod_ctx->qcompress = 0.6f;
ff->cod_ctx->qmin = 10;
ff->cod_ctx->qmax = 51;
ff->cod_ctx->max_qdiff = 4;
ff->cod_ctx->max_b_frames = 3;
ff->cod_ctx->refs = 5;
ff->cod_ctx->directpred = 3;
ff->cod_ctx->trellis = 1;
ff->cod_ctx->flags2 |= CODEC_FLAG2_BPYRAMID | CODEC_FLAG2_MIXED_REFS
| CODEC_FLAG2_WPRED | CODEC_FLAG2_8X8DCT
| CODEC_FLAG2_FASTPSKIP;
ff->cod_ctx->weighted_p_pred = 2;
ff->cod_ctx->rc_lookahead = 50;
}
if (ff->fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
ff->cod_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;

if (av_set_parameters(ff->fmt_ctx, NULL) < 0)
goto error;

ff->codec = avcodec_find_encoder(ff->cod_ctx->codec_id);
if (!ff->codec)
goto error;
if (avcodec_open(ff->cod_ctx, ff->codec) < 0)
goto error;

ff->frame = avcodec_alloc_frame();
if (!ff->frame)
goto error;
tmp = (uint8_t *)av_malloc(avpicture_get_size(ff->cod_ctx->pix_fmt,
ff->cod_ctx->width,
ff->cod_ctx->height));
if (!tmp)
goto error;
avpicture_fill((AVPicture *)ff->frame, tmp, ff->cod_ctx->pix_fmt,
ff->cod_ctx->width, ff->cod_ctx->height);

if (!(ff->fmt_ctx->flags & AVFMT_NOFILE))
if (url_fopen(&ff->fmt_ctx->pb, file, URL_WRONLY) < 0)
goto error;

ff->buf_len = 64 * 1024 * 1024;
ff->buf = (uint8_t *)av_malloc(ff->buf_len);

ff->src_fmt = rgb ? PIX_FMT_RGB32 : PIX_FMT_YUV444P;

av_write_header(ff->fmt_ctx);

return seq;

error:
pipi_close_sequence(seq);
return NULL;

#else
return NULL;

#endif
}

int pipi_feed_sequence(pipi_sequence_t *seq, uint8_t const *buffer,
int width, int height)
{
#if defined USE_FFMPEG
AVPacket packet;
uint8_t const *buflist[3];
int pitchlist[3];
size_t bytes;
int n;

ffmpeg_codec_t *ff = (ffmpeg_codec_t *)seq->codec_priv;

if (ff->src_width != width || ff->src_height != height)
{
ff->src_width = width;
ff->src_height = height;
if (ff->sws_ctx)
sws_freeContext(ff->sws_ctx);
ff->sws_ctx = NULL;
}

if (!ff->sws_ctx)
{
ff->sws_ctx = sws_getContext(width, height, ff->src_fmt,
ff->cod_ctx->width,
ff->cod_ctx->height,
ff->cod_ctx->pix_fmt, SWS_BICUBIC,
NULL, NULL, NULL);
if (seq->convert_buf)
{
free(seq->convert_buf);
seq->convert_buf = NULL;
}
}
if (!ff->sws_ctx)
return -1;

/* Convert interleaved YUV to planar YUV */
if (ff->src_fmt == PIX_FMT_YUV444P)
{
if (!seq->convert_buf)
seq->convert_buf = malloc(width * height * 3);

for (n = 0; n < width * height; n++)
{
seq->convert_buf[n] = buffer[4 * n];
seq->convert_buf[n + width * height] = buffer[4 * n + 1];
seq->convert_buf[n + 2 * width * height] = buffer[4 * n + 2];
}

/* Feed the buffers to FFmpeg */
buflist[0] = seq->convert_buf;
buflist[1] = seq->convert_buf + 2 * width * height;
buflist[2] = seq->convert_buf + width * height;
pitchlist[0] = pitchlist[1] = pitchlist[2] = width;
}
else
{
buflist[0] = buffer;
pitchlist[0] = 4 * width;
}

sws_scale(ff->sws_ctx, buflist, pitchlist, 0, height,
ff->frame->data, ff->frame->linesize);

bytes = avcodec_encode_video(ff->cod_ctx, ff->buf,
ff->buf_len, ff->frame);
if (bytes <= 0)
return 0;

av_init_packet(&packet);
if (ff->cod_ctx->coded_frame->pts != 0x8000000000000000LL)
packet.pts = av_rescale_q(ff->cod_ctx->coded_frame->pts,
ff->cod_ctx->time_base, ff->stream->time_base);
if (ff->cod_ctx->coded_frame->key_frame)
packet.flags |= PKT_FLAG_KEY;
packet.stream_index = 0;
packet.data = ff->buf;
packet.size = bytes;

if (av_interleaved_write_frame(ff->fmt_ctx, &packet) < 0)
return -1;
#endif

return 0;
}

int pipi_close_sequence(pipi_sequence_t *seq)
{
#if defined USE_FFMPEG
ffmpeg_codec_t *ff = (ffmpeg_codec_t *)seq->codec_priv;

/* Finish the sequence */
if (ff->buf)
{
av_write_trailer(ff->fmt_ctx);
}

/* Close everything */
if (ff->buf)
{
av_free(ff->buf);
ff->buf = NULL;
}

if (ff->cod_ctx)
{
avcodec_close(ff->cod_ctx);
ff->cod_ctx = NULL;
}

if (ff->frame)
{
av_free(ff->frame->data[0]);
av_free(ff->frame);
ff->frame = NULL;
}

if (ff->fmt_ctx)
{
av_freep(&ff->fmt_ctx->streams[0]->codec);
ff->codec = NULL;

av_freep(&ff->fmt_ctx->streams[0]);
ff->stream = NULL;

if (!(ff->fmt_ctx->flags & AVFMT_NOFILE) && ff->fmt_ctx->pb)
url_fclose(ff->fmt_ctx->pb);

av_free(ff->fmt_ctx);
ff->fmt_ctx = NULL;
}

if (ff->sws_ctx)
{
sws_freeContext(ff->sws_ctx);
ff->sws_ctx = NULL;
ff->src_width = ff->src_height = 0;
}

free(ff);
free(seq);
#endif

return 0;
}

+ 201
- 0
src/image/stock.cpp Voir le fichier

@@ -0,0 +1,201 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* stock.c: stock images
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_load_stock(char const *name)
{
pipi_image_t *ret;
pipi_pixels_t *pix;

/* Generate a Bayer dithering pattern. */
if(!strncmp(name, "bayer:", 6))
{
int w, h = 0;

w = atoi(name + 6);
name = strchr(name + 6, 'x');
if(name)
h = atoi(name + 1);
if(!h)
h = w;

return pipi_render_bayer(w, h);
}

/* Generate a clustered dithering pattern. */
if(!strncmp(name, "halftone:", 9))
{
int w, h = 0;

w = atoi(name + 9);
name = strchr(name + 9, 'x');
if(name)
h = atoi(name + 1);
if(!h)
h = w;

return pipi_render_halftone(w, h);
}

/* Generate an error diffusion matrix. */
if(!strncmp(name, "ediff:", 6))
{
float const *ker;
int w, h;

if(!strcmp(name + 6, "fs"))
{
static float const myker[] =
{
0., 1., 7./16,
3./16, 5./16, 1./16,
};
ker = myker; w = 3; h = 2;
}
else if(!strcmp(name + 6, "jajuni"))
{
static float const myker[] =
{
0., 0., 1., 7./48, 5./48,
3./48, 5./48, 7./48, 5./48, 3./48,
1./48, 3./48, 5./48, 3./48, 1./48,
};
ker = myker; w = 5; h = 3;
}
else if(!strcmp(name + 6, "atkinson"))
{
static float const myker[] =
{
0., 1., 1./8, 1./8,
1./8, 1./8, 1./8, 0.,
0., 1./8, 0., 0.,
};
ker = myker; w = 4; h = 3;
}
else if(!strcmp(name + 6, "fan"))
{
static float const myker[] =
{
0., 0., 1., 7./16,
1./16, 3./16, 5./16, 0.,
};
ker = myker; w = 4; h = 2;
}
else if(!strcmp(name + 6, "shiaufan"))
{
static float const myker[] =
{
0., 0., 1., 1./2,
1./8, 1./8, 1./4, 0.,
};
ker = myker; w = 4; h = 2;
}
else if(!strcmp(name + 6, "shiaufan2"))
{
static float const myker[] =
{
0., 0., 0., 1., 1./2,
1./16, 1./16, 1./8, 1./4, 0.,
};
ker = myker; w = 5; h = 2;
}
else if(!strcmp(name + 6, "stucki"))
{
static float const myker[] =
{
0., 0., 1., 8./42, 4./42,
2./42, 4./42, 8./42, 4./42, 2./42,
1./42, 2./42, 4./42, 2./42, 1./42,
};
ker = myker; w = 5; h = 3;
}
else if(!strcmp(name + 6, "burkes"))
{
static float const myker[] =
{
0., 0., 1., 4./16, 2./16,
1./16, 2./16, 4./16, 2./16, 1./16,
};
ker = myker; w = 5; h = 2;
}
else if(!strcmp(name + 6, "sierra"))
{
static float const myker[] =
{
0., 0., 1., 5./32, 3./32,
2./32, 4./32, 5./32, 4./32, 2./32,
0., 2./32, 3./32, 2./32, 0.,
};
ker = myker; w = 5; h = 3;
}
else if(!strcmp(name + 6, "sierra2"))
{
static float const myker[] =
{
0., 0., 1., 4./16, 3./16,
1./16, 2./16, 3./16, 2./16, 1./16,
};
ker = myker; w = 5; h = 2;
}
else if(!strcmp(name + 6, "lite"))
{
static float const myker[] =
{
0., 1., 1./2,
1./4, 1./4, 0.,
};
ker = myker; w = 3; h = 2;
}
else
return NULL;

ret = pipi_new(w, h);
pix = pipi_get_pixels(ret, PIPI_PIXELS_Y_F32);
memcpy(pix->pixels, ker, w * h * sizeof(float));

return ret;
}

/* Generate a completely random image. */
if(!strncmp(name, "random:", 7))
{
int w, h = 0;

w = atoi(name + 7);
name = strchr(name + 7, 'x');
if(name)
h = atoi(name + 1);
if(!h)
h = w;
if(w <= 0 || h <= 0)
return NULL;

return pipi_render_random(w, h);
}

return NULL;
}


+ 111
- 0
src/image/tiles.cpp Voir le fichier

@@ -0,0 +1,111 @@
/*
* libpipi Pathetic image processing interface 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.
*/

/*
* tiles.c: the tiles system
*/

#include "config.h"

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

#include "pipi.h"
#include "pipi-internals.h"

#ifdef USE_TILES
pipi_tile_t *pipi_get_tile(pipi_image_t *img, int x, int y, int zoom,
pipi_format_t fmt, int plane)
{
pipi_tile_t * ret;
int i;

/* If the tile already exists, return it. */
for(i = 0; i < img->ntiles; i++)
{
if(img->tiles[i]->x == x && img->tiles[i]->y == y
&& img->tiles[i]->fmt == fmt
&& img->tiles[i]->zoom == zoom
&& img->tiles[i]->plane == plane)
{
img->tiles[i]->refcount++;
return img->tiles[i];
}
}

/* Create a tile. When the image provides a tile creation function,
* we should use it. */
ret = pipi_create_tile(fmt, plane);
ret->x = x;
ret->y = y;
ret->refcount = 1;

/* Insert tile and return it. */
img->ntiles++;
img->tiles = realloc(img->tiles, img->ntiles * sizeof(pipi_tile_t *));
img->tiles[img->ntiles - 1] = ret;

return ret;
}

void pipi_release_tile(pipi_image_t *img, pipi_tile_t *tile)
{
int i;

for(i = 0; i < img->ntiles; i++)
{
if(img->tiles[i] == tile)
{
img->tiles[i]->refcount--;
if(img->tiles[i]->refcount <= 0)
{
free(img->tiles[i]);
img->tiles[i] = img->tiles[img->ntiles - 1];
img->ntiles--;
}
return;
}
}
}

pipi_tile_t *pipi_create_tile(pipi_format_t fmt, int plane)
{
pipi_tile_t * ret;
size_t bytes;

switch(fmt)
{
case PIPI_PIXELS_RGBA_U8:
case PIPI_PIXELS_BGR_U8:
bytes = sizeof(uint8_t) * TILE_SIZE * TILE_SIZE;
break;
case PIPI_PIXELS_RGBA_F32:
case PIPI_PIXELS_Y_F32:
bytes = sizeof(float) * TILE_SIZE * TILE_SIZE;
break;
default:
bytes = 0;
break;
}

ret = malloc(sizeof(pipi_tile_t) + bytes);
ret->fmt = fmt;
ret->plane = plane;
ret->data.u8 = ret->align.u8;

return ret;
}
#endif /* USE_TILES */


Chargement…
Annuler
Enregistrer