/*
 * The Python Imaging Library
 * $Id: RankFilter.c 2929 2006-11-22 21:12:55Z fredrik $
 *
 * min, max, median filters
 *
 * history:
 * 2002-06-08 fl    Created
 *
 * Copyright (c) Secret Labs AB 2002.  All rights reserved.
 *
 * See the README file for information on usage and redistribution.
 */

#include "Imaging.h"

/* Fast rank algorithm (due to Wirth), based on public domain code
   by Nicolas Devillard, available at http://ndevilla.free.fr */

#define SWAP(type,a,b) { register type t=(a);(a)=(b);(b)=t; }

#define MakeRankFunction(type)\
static type Rank##type(type a[], int n, int k)\
{\
    register int i, j, l, m;\
    register type x;\
    l = 0; m = n-1;\
    while (l < m) {\
        x = a[k];\
        i = l;\
        j = m;\
        do {\
            while (a[i] < x) i++;\
            while (x < a[j]) j--;\
            if (i <= j) {\
                SWAP(type, a[i], a[j]);\
                i++; j--;\
            }\
        } while (i <= j);\
        if (j < k) l = i;\
        if (k < i) m = j;\
    }\
    return a[k];\
}

MakeRankFunction(UINT8)
MakeRankFunction(INT32)
MakeRankFunction(FLOAT32)

Imaging
ImagingRankFilter(Imaging im, int size, int rank)
{
    Imaging imOut = NULL;
    int x, y;
    int i, margin, size2;

    if (!im || im->bands != 1 || im->type == IMAGING_TYPE_SPECIAL)
	return (Imaging) ImagingError_ModeError();

    if (!(size & 1))
	return (Imaging) ImagingError_ValueError("bad filter size");

    size2 = size * size;
    margin = (size-1) / 2;

    if (rank < 0 || rank >= size2)
	return (Imaging) ImagingError_ValueError("bad rank value");

    imOut = ImagingNew(im->mode, im->xsize - 2*margin, im->ysize - 2*margin);
    if (!imOut)
	return NULL;

#define RANK_BODY(type) do {\
    type* buf = malloc(size2 * sizeof(type));\
    if (!buf)\
        goto nomemory;\
    for (y = 0; y < imOut->ysize; y++)\
        for (x = 0; x < imOut->xsize; x++) {\
            for (i = 0; i < size; i++)\
                memcpy(buf + i*size, &IMAGING_PIXEL_##type(im, x, y+i),\
                       size * sizeof(type));\
            IMAGING_PIXEL_##type(imOut, x, y) = Rank##type(buf, size2, rank);\
        }\
} while (0)

    if (im->image8)
        RANK_BODY(UINT8);
    else if (im->type == IMAGING_TYPE_INT32)
        RANK_BODY(INT32);
    else if (im->type == IMAGING_TYPE_FLOAT32)
        RANK_BODY(FLOAT32);
    else {
        /* safety net (we shouldn't end up here) */
        ImagingDelete(imOut);
        return (Imaging) ImagingError_ModeError();
    }
    
    ImagingCopyInfo(imOut, im);

    return imOut;

nomemory:
    ImagingDelete(imOut);
    return (Imaging) ImagingError_MemoryError();
}
