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