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