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 */ | |||||