merged iteratively with our current image processing code.undefined
@@ -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]; | |||
} |
@@ -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; | |||
} |
@@ -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); | |||
} | |||
@@ -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; | |||
} | |||
@@ -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 |
@@ -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 |
@@ -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. | |||
*/ | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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"); | |||
} | |||
@@ -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); | |||
} | |||
@@ -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; | |||
} |
@@ -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; | |||
} | |||
@@ -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); | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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); | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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 | |||
@@ -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; | |||
} | |||
@@ -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 | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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 | |||
@@ -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); | |||
} | |||
@@ -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; | |||
} | |||
@@ -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__ */ | |||
@@ -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__ */ | |||
@@ -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 | |||
@@ -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__ */ | |||
@@ -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; | |||
} | |||
@@ -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__ */ | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; | |||
} |
@@ -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; | |||
} | |||
@@ -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 */ | |||