From ce1d39cc3a6b2ebf0d95cae8354b888dc062ed3c Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Fri, 13 Jun 2014 06:35:19 +0000 Subject: [PATCH] image: import old libpipi code, hoping it can be upgraded to C++ and merged iteratively with our current image processing code. --- src/image/accessors.cpp | 65 +++ src/image/analysis/histogram.cpp | 178 ++++++++ src/image/analysis/measure.cpp | 101 ++++ src/image/codec.cpp | 124 +++++ src/image/codec/coreimage.cpp | 180 ++++++++ src/image/codec/coreimage.h | 38 ++ src/image/codec/gdi.cpp | 148 ++++++ src/image/codec/imlib.cpp | 120 +++++ src/image/codec/jpeg.cpp | 249 ++++++++++ src/image/codec/opencv.cpp | 112 +++++ src/image/codec/oric.cpp | 588 ++++++++++++++++++++++++ src/image/codec/sdl.cpp | 133 ++++++ src/image/colorstring.cpp | 127 ++++++ src/image/combine/blit.cpp | 89 ++++ src/image/combine/merge.cpp | 86 ++++ src/image/combine/minmax.cpp | 123 +++++ src/image/combine/mulscreen.cpp | 221 +++++++++ src/image/combine/rgb.cpp | 135 ++++++ src/image/combine/subadd.cpp | 173 +++++++ src/image/context.cpp | 736 ++++++++++++++++++++++++++++++ src/image/crop.cpp | 68 +++ src/image/dither.cpp | 93 ++++ src/image/dither/dbs.cpp | 228 +++++++++ src/image/dither/ediff.cpp | 86 ++++ src/image/dither/ordered.cpp | 144 ++++++ src/image/dither/ostromoukhov.cpp | 117 +++++ src/image/dither/random.cpp | 60 +++ src/image/filter/autocontrast.cpp | 111 +++++ src/image/filter/blur.cpp | 315 +++++++++++++ src/image/filter/color.cpp | 288 ++++++++++++ src/image/filter/convolution.cpp | 289 ++++++++++++ src/image/filter/dilate.cpp | 170 +++++++ src/image/filter/median.cpp | 134 ++++++ src/image/filter/rotate.cpp | 100 ++++ src/image/filter/sharpen.cpp | 128 ++++++ src/image/filter/transform.cpp | 252 ++++++++++ src/image/filter/wave.cpp | 177 +++++++ src/image/filter/yuv.cpp | 127 ++++++ src/image/paint/bezier.cpp | 58 +++ src/image/paint/floodfill.cpp | 318 +++++++++++++ src/image/paint/line.cpp | 470 +++++++++++++++++++ src/image/paint/rectangle.cpp | 47 ++ src/image/paint/tile.cpp | 56 +++ src/image/pipi-internals.h | 138 ++++++ src/image/pipi-stubs.h | 97 ++++ src/image/pipi-template.h | 80 ++++ src/image/pipi-types.h | 100 ++++ src/image/pipi.cpp | 96 ++++ src/image/pipi.h | 253 ++++++++++ src/image/pixels.cpp | 285 ++++++++++++ src/image/quantize/reduce.cpp | 509 +++++++++++++++++++++ src/image/render/noise.cpp | 61 +++ src/image/render/screen.cpp | 128 ++++++ src/image/resample/bicubic.cpp | 136 ++++++ src/image/resample/bresenham.cpp | 131 ++++++ src/image/sequence.cpp | 346 ++++++++++++++ src/image/stock.cpp | 201 ++++++++ src/image/tiles.cpp | 111 +++++ 58 files changed, 10234 insertions(+) create mode 100644 src/image/accessors.cpp create mode 100644 src/image/analysis/histogram.cpp create mode 100644 src/image/analysis/measure.cpp create mode 100644 src/image/codec.cpp create mode 100644 src/image/codec/coreimage.cpp create mode 100644 src/image/codec/coreimage.h create mode 100644 src/image/codec/gdi.cpp create mode 100644 src/image/codec/imlib.cpp create mode 100644 src/image/codec/jpeg.cpp create mode 100644 src/image/codec/opencv.cpp create mode 100644 src/image/codec/oric.cpp create mode 100644 src/image/codec/sdl.cpp create mode 100644 src/image/colorstring.cpp create mode 100644 src/image/combine/blit.cpp create mode 100644 src/image/combine/merge.cpp create mode 100644 src/image/combine/minmax.cpp create mode 100644 src/image/combine/mulscreen.cpp create mode 100644 src/image/combine/rgb.cpp create mode 100644 src/image/combine/subadd.cpp create mode 100644 src/image/context.cpp create mode 100644 src/image/crop.cpp create mode 100644 src/image/dither.cpp create mode 100644 src/image/dither/dbs.cpp create mode 100644 src/image/dither/ediff.cpp create mode 100644 src/image/dither/ordered.cpp create mode 100644 src/image/dither/ostromoukhov.cpp create mode 100644 src/image/dither/random.cpp create mode 100644 src/image/filter/autocontrast.cpp create mode 100644 src/image/filter/blur.cpp create mode 100644 src/image/filter/color.cpp create mode 100644 src/image/filter/convolution.cpp create mode 100644 src/image/filter/dilate.cpp create mode 100644 src/image/filter/median.cpp create mode 100644 src/image/filter/rotate.cpp create mode 100644 src/image/filter/sharpen.cpp create mode 100644 src/image/filter/transform.cpp create mode 100644 src/image/filter/wave.cpp create mode 100644 src/image/filter/yuv.cpp create mode 100644 src/image/paint/bezier.cpp create mode 100644 src/image/paint/floodfill.cpp create mode 100644 src/image/paint/line.cpp create mode 100644 src/image/paint/rectangle.cpp create mode 100644 src/image/paint/tile.cpp create mode 100644 src/image/pipi-internals.h create mode 100644 src/image/pipi-stubs.h create mode 100644 src/image/pipi-template.h create mode 100644 src/image/pipi-types.h create mode 100644 src/image/pipi.cpp create mode 100644 src/image/pipi.h create mode 100644 src/image/pixels.cpp create mode 100644 src/image/quantize/reduce.cpp create mode 100644 src/image/render/noise.cpp create mode 100644 src/image/render/screen.cpp create mode 100644 src/image/resample/bicubic.cpp create mode 100644 src/image/resample/bresenham.cpp create mode 100644 src/image/sequence.cpp create mode 100644 src/image/stock.cpp create mode 100644 src/image/tiles.cpp diff --git a/src/image/accessors.cpp b/src/image/accessors.cpp new file mode 100644 index 00000000..bc2f5f8f --- /dev/null +++ b/src/image/accessors.cpp @@ -0,0 +1,65 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux + * 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 +#include +#include + +#include + +#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]; +} diff --git a/src/image/analysis/histogram.cpp b/src/image/analysis/histogram.cpp new file mode 100644 index 00000000..2435b3b2 --- /dev/null +++ b/src/image/analysis/histogram.cpp @@ -0,0 +1,178 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux +#include +#include + +#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; +} diff --git a/src/image/analysis/measure.cpp b/src/image/analysis/measure.cpp new file mode 100644 index 00000000..50468b1e --- /dev/null +++ b/src/image/analysis/measure.cpp @@ -0,0 +1,101 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + +#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); +} + diff --git a/src/image/codec.cpp b/src/image/codec.cpp new file mode 100644 index 00000000..02b4ea6f --- /dev/null +++ b/src/image/codec.cpp @@ -0,0 +1,124 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#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; +} + diff --git a/src/image/codec/coreimage.cpp b/src/image/codec/coreimage.cpp new file mode 100644 index 00000000..080823f2 --- /dev/null +++ b/src/image/codec/coreimage.cpp @@ -0,0 +1,180 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux + * 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; yp[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 diff --git a/src/image/codec/coreimage.h b/src/image/codec/coreimage.h new file mode 100644 index 00000000..cb0f0032 --- /dev/null +++ b/src/image/codec/coreimage.h @@ -0,0 +1,38 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux + * 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 +#include +#include + +#import + +#include "pipi.h" +#include "pipi-internals.h" + +struct pipi_codec_coreimage +{ + NSBitmapFormat format; +}; + + +#endif diff --git a/src/image/codec/gdi.cpp b/src/image/codec/gdi.cpp new file mode 100644 index 00000000..08d0690b --- /dev/null +++ b/src/image/codec/gdi.cpp @@ -0,0 +1,148 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#include + +#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. + */ + diff --git a/src/image/codec/imlib.cpp b/src/image/codec/imlib.cpp new file mode 100644 index 00000000..0ed3bf65 --- /dev/null +++ b/src/image/codec/imlib.cpp @@ -0,0 +1,120 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#include + +#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; +} + diff --git a/src/image/codec/jpeg.cpp b/src/image/codec/jpeg.cpp new file mode 100644 index 00000000..862262de --- /dev/null +++ b/src/image/codec/jpeg.cpp @@ -0,0 +1,249 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux + * 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 +#include +#include +#include +#include + +#include + +#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; +} + diff --git a/src/image/codec/opencv.cpp b/src/image/codec/opencv.cpp new file mode 100644 index 00000000..04d60f4e --- /dev/null +++ b/src/image/codec/opencv.cpp @@ -0,0 +1,112 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#include +#include + +#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; +} + diff --git a/src/image/codec/oric.cpp b/src/image/codec/oric.cpp new file mode 100644 index 00000000..45244f79 --- /dev/null +++ b/src/image/codec/oric.cpp @@ -0,0 +1,588 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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"); +} + diff --git a/src/image/codec/sdl.cpp b/src/image/codec/sdl.cpp new file mode 100644 index 00000000..26d63ccc --- /dev/null +++ b/src/image/codec/sdl.cpp @@ -0,0 +1,133 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#include + +#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); +} + diff --git a/src/image/colorstring.cpp b/src/image/colorstring.cpp new file mode 100644 index 00000000..357d62a6 --- /dev/null +++ b/src/image/colorstring.cpp @@ -0,0 +1,127 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux +#include +#include +#include + +#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; ipixel_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; +} diff --git a/src/image/combine/blit.cpp b/src/image/combine/blit.cpp new file mode 100644 index 00000000..9a08b36d --- /dev/null +++ b/src/image/combine/blit.cpp @@ -0,0 +1,89 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + +#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; +} + diff --git a/src/image/combine/merge.cpp b/src/image/combine/merge.cpp new file mode 100644 index 00000000..beeab483 --- /dev/null +++ b/src/image/combine/merge.cpp @@ -0,0 +1,86 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + +#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); +} + diff --git a/src/image/combine/minmax.cpp b/src/image/combine/minmax.cpp new file mode 100644 index 00000000..780c03a4 --- /dev/null +++ b/src/image/combine/minmax.cpp @@ -0,0 +1,123 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + +#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; +} + diff --git a/src/image/combine/mulscreen.cpp b/src/image/combine/mulscreen.cpp new file mode 100644 index 00000000..d50e8ccc --- /dev/null +++ b/src/image/combine/mulscreen.cpp @@ -0,0 +1,221 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + +#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; +} + diff --git a/src/image/combine/rgb.cpp b/src/image/combine/rgb.cpp new file mode 100644 index 00000000..7a6d5dcc --- /dev/null +++ b/src/image/combine/rgb.cpp @@ -0,0 +1,135 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + +#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; +} + diff --git a/src/image/combine/subadd.cpp b/src/image/combine/subadd.cpp new file mode 100644 index 00000000..f3e07786 --- /dev/null +++ b/src/image/combine/subadd.cpp @@ -0,0 +1,173 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include + +#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; +} + diff --git a/src/image/context.cpp b/src/image/context.cpp new file mode 100644 index 00000000..2c77c2f0 --- /dev/null +++ b/src/image/context.cpp @@ -0,0 +1,736 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/crop.cpp b/src/image/crop.cpp new file mode 100644 index 00000000..3306c4b4 --- /dev/null +++ b/src/image/crop.cpp @@ -0,0 +1,68 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2009 Sam Hocevar + * 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 +#include + +#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; +} + diff --git a/src/image/dither.cpp b/src/image/dither.cpp new file mode 100644 index 00000000..2199a685 --- /dev/null +++ b/src/image/dither.cpp @@ -0,0 +1,93 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#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); +} + diff --git a/src/image/dither/dbs.cpp b/src/image/dither/dbs.cpp new file mode 100644 index 00000000..647f973c --- /dev/null +++ b/src/image/dither/dbs.cpp @@ -0,0 +1,228 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include + +#include + +#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; +} + diff --git a/src/image/dither/ediff.cpp b/src/image/dither/ediff.cpp new file mode 100644 index 00000000..efd259c1 --- /dev/null +++ b/src/image/dither/ediff.cpp @@ -0,0 +1,86 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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; +} + diff --git a/src/image/dither/ordered.cpp b/src/image/dither/ordered.cpp new file mode 100644 index 00000000..e459e8ac --- /dev/null +++ b/src/image/dither/ordered.cpp @@ -0,0 +1,144 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#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; +} + diff --git a/src/image/dither/ostromoukhov.cpp b/src/image/dither/ostromoukhov.cpp new file mode 100644 index 00000000..a921addc --- /dev/null +++ b/src/image/dither/ostromoukhov.cpp @@ -0,0 +1,117 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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; +} + diff --git a/src/image/dither/random.cpp b/src/image/dither/random.cpp new file mode 100644 index 00000000..b138e8ec --- /dev/null +++ b/src/image/dither/random.cpp @@ -0,0 +1,60 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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; +} + diff --git a/src/image/filter/autocontrast.cpp b/src/image/filter/autocontrast.cpp new file mode 100644 index 00000000..19f797c6 --- /dev/null +++ b/src/image/filter/autocontrast.cpp @@ -0,0 +1,111 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/filter/blur.cpp b/src/image/filter/blur.cpp new file mode 100644 index 00000000..27e5d360 --- /dev/null +++ b/src/image/filter/blur.cpp @@ -0,0 +1,315 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include +#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 + diff --git a/src/image/filter/color.cpp b/src/image/filter/color.cpp new file mode 100644 index 00000000..989c21d4 --- /dev/null +++ b/src/image/filter/color.cpp @@ -0,0 +1,288 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/filter/convolution.cpp b/src/image/filter/convolution.cpp new file mode 100644 index 00000000..5aca7d8e --- /dev/null +++ b/src/image/filter/convolution.cpp @@ -0,0 +1,289 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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 + diff --git a/src/image/filter/dilate.cpp b/src/image/filter/dilate.cpp new file mode 100644 index 00000000..9cd04e8a --- /dev/null +++ b/src/image/filter/dilate.cpp @@ -0,0 +1,170 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/filter/median.cpp b/src/image/filter/median.cpp new file mode 100644 index 00000000..f11dfcb1 --- /dev/null +++ b/src/image/filter/median.cpp @@ -0,0 +1,134 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/filter/rotate.cpp b/src/image/filter/rotate.cpp new file mode 100644 index 00000000..362054f2 --- /dev/null +++ b/src/image/filter/rotate.cpp @@ -0,0 +1,100 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include +#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; +} + diff --git a/src/image/filter/sharpen.cpp b/src/image/filter/sharpen.cpp new file mode 100644 index 00000000..65184498 --- /dev/null +++ b/src/image/filter/sharpen.cpp @@ -0,0 +1,128 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include +#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; +} + diff --git a/src/image/filter/transform.cpp b/src/image/filter/transform.cpp new file mode 100644 index 00000000..51cd43b8 --- /dev/null +++ b/src/image/filter/transform.cpp @@ -0,0 +1,252 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/filter/wave.cpp b/src/image/filter/wave.cpp new file mode 100644 index 00000000..acb3a032 --- /dev/null +++ b/src/image/filter/wave.cpp @@ -0,0 +1,177 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include +#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; +} + diff --git a/src/image/filter/yuv.cpp b/src/image/filter/yuv.cpp new file mode 100644 index 00000000..a60056fd --- /dev/null +++ b/src/image/filter/yuv.cpp @@ -0,0 +1,127 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/paint/bezier.cpp b/src/image/paint/bezier.cpp new file mode 100644 index 00000000..70cc0af6 --- /dev/null +++ b/src/image/paint/bezier.cpp @@ -0,0 +1,58 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux +#include +#include + +#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; +} diff --git a/src/image/paint/floodfill.cpp b/src/image/paint/floodfill.cpp new file mode 100644 index 00000000..93a36ade --- /dev/null +++ b/src/image/paint/floodfill.cpp @@ -0,0 +1,318 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux + * 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 +#include +#include +#include + +#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; +} diff --git a/src/image/paint/line.cpp b/src/image/paint/line.cpp new file mode 100644 index 00000000..52f597d2 --- /dev/null +++ b/src/image/paint/line.cpp @@ -0,0 +1,470 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux + * 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 +#include +#include + +#include + +#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) 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)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 + diff --git a/src/image/paint/rectangle.cpp b/src/image/paint/rectangle.cpp new file mode 100644 index 00000000..9eac87c8 --- /dev/null +++ b/src/image/paint/rectangle.cpp @@ -0,0 +1,47 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 2008 Jean-Yves Lamoureux + * 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 +#include +#include + +#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); +} + diff --git a/src/image/paint/tile.cpp b/src/image/paint/tile.cpp new file mode 100644 index 00000000..75c93afb --- /dev/null +++ b/src/image/paint/tile.cpp @@ -0,0 +1,56 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + +#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; +} + diff --git a/src/image/pipi-internals.h b/src/image/pipi-internals.h new file mode 100644 index 00000000..9b43fa35 --- /dev/null +++ b/src/image/pipi-internals.h @@ -0,0 +1,138 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2009 Sam Hocevar + * 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__ */ + diff --git a/src/image/pipi-stubs.h b/src/image/pipi-stubs.h new file mode 100644 index 00000000..55e7c9b6 --- /dev/null +++ b/src/image/pipi-stubs.h @@ -0,0 +1,97 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2006 Sam Hocevar + * 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 +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 +# include +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 +# elif defined HAVE_NETINET_IN_H +# include +# endif +# define hton16 htons +# define hton32 htonl +#else +# if defined __KERNEL__ + /* Nothing to do */ +# elif defined HAVE_ENDIAN_H +# include +# 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__ */ + diff --git a/src/image/pipi-template.h b/src/image/pipi-template.h new file mode 100644 index 00000000..0e3e364a --- /dev/null +++ b/src/image/pipi-template.h @@ -0,0 +1,80 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 + diff --git a/src/image/pipi-types.h b/src/image/pipi-types.h new file mode 100644 index 00000000..139c68e9 --- /dev/null +++ b/src/image/pipi-types.h @@ -0,0 +1,100 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2008 Sam Hocevar + * 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 header is present, just include it */ +#if PIPI_TYPES == 1 +# include +# include + +/* mode 2: standard header is present, just include it */ +#elif PIPI_TYPES == 2 +# include +# include + +/* mode 3: indicates Win32, only (u)intptr_t is present + * FIXME: Win64 probably doesn't work that way */ +#elif PIPI_TYPES == 3 +#include + +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__ */ + diff --git a/src/image/pipi.cpp b/src/image/pipi.cpp new file mode 100644 index 00000000..67ce3b14 --- /dev/null +++ b/src/image/pipi.cpp @@ -0,0 +1,96 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#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; +} + diff --git a/src/image/pipi.h b/src/image/pipi.h new file mode 100644 index 00000000..8340e859 --- /dev/null +++ b/src/image/pipi.h @@ -0,0 +1,253 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2009 Sam Hocevar + * 2008 Jean-Yves Lamoureux + +#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__ */ + diff --git a/src/image/pixels.cpp b/src/image/pixels.cpp new file mode 100644 index 00000000..44506580 --- /dev/null +++ b/src/image/pixels.cpp @@ -0,0 +1,285 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#include + +#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; +} + diff --git a/src/image/quantize/reduce.cpp b/src/image/quantize/reduce.cpp new file mode 100644 index 00000000..cfeea980 --- /dev/null +++ b/src/image/quantize/reduce.cpp @@ -0,0 +1,509 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include +#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; +} + diff --git a/src/image/render/noise.cpp b/src/image/render/noise.cpp new file mode 100644 index 00000000..6a736ae5 --- /dev/null +++ b/src/image/render/noise.cpp @@ -0,0 +1,61 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#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; +} + diff --git a/src/image/render/screen.cpp b/src/image/render/screen.cpp new file mode 100644 index 00000000..3b5671dc --- /dev/null +++ b/src/image/render/screen.cpp @@ -0,0 +1,128 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include +#include + +#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; +} + diff --git a/src/image/resample/bicubic.cpp b/src/image/resample/bicubic.cpp new file mode 100644 index 00000000..6cf36658 --- /dev/null +++ b/src/image/resample/bicubic.cpp @@ -0,0 +1,136 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2010 Sam Hocevar + * 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 +#include + +#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; +} + diff --git a/src/image/resample/bresenham.cpp b/src/image/resample/bresenham.cpp new file mode 100644 index 00000000..1611cfac --- /dev/null +++ b/src/image/resample/bresenham.cpp @@ -0,0 +1,131 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2009 Sam Hocevar + * 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 +#include + +#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; +} + diff --git a/src/image/sequence.cpp b/src/image/sequence.cpp new file mode 100644 index 00000000..7cd97b35 --- /dev/null +++ b/src/image/sequence.cpp @@ -0,0 +1,346 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2009 Sam Hocevar + * 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 +#include +#include + +#if defined USE_FFMPEG +# include +# include +#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; +} diff --git a/src/image/stock.cpp b/src/image/stock.cpp new file mode 100644 index 00000000..6184bcea --- /dev/null +++ b/src/image/stock.cpp @@ -0,0 +1,201 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#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; +} + diff --git a/src/image/tiles.cpp b/src/image/tiles.cpp new file mode 100644 index 00000000..24eea793 --- /dev/null +++ b/src/image/tiles.cpp @@ -0,0 +1,111 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2008 Sam Hocevar + * 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 +#include +#include + +#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 */ +