Some cleanup in rasterops code and added colour inversion function.

Fixed ugly bugs in Cairo surface composition onto video frame.

Originally committed to SVN as r1481.
This commit is contained in:
Niels Martin Hansen 2007-08-13 01:06:27 +00:00
parent c68a782820
commit 7e8fcbdc8f
4 changed files with 92 additions and 54 deletions

View File

@ -143,14 +143,24 @@ raster.gaussian_blur(surface, sigma)
Applies a strength sigma gaussian blur on the surface.
raster.box(surface, size, repetitions)
raster.box_blur(surface, size, repetitions)
Applies a box filter (blur) of variable size to the surface as many times as
Applies a box blur filter of variable size to the surface as many times as
specified. Please note that no specific optimisation is done for applying
box filters over more general filters and using box blur over gaussian blur
is probably no faster and might even be slower.
raster.invert(surface)
Invert the colour in the given surface.
For ARGB32 surfaces, only the colour channels are inverted, the alpha
channel is kept as-is.
For RGB24 surfaces, all channels are inverted.
For A8 and A1 surfaces the alpha is inverted.
More filtering functions are planned, though no specifics yet.
Wishes/suggestions are welcome, and so are patches to add more functions.

View File

@ -1,5 +1,6 @@
function render_frame(f, t)
local surf = f.create_cairo_surface()
raster.gaussian_blur(surf, t)--1+(1-math.cos(t*10))*2)
raster.invert(surf)
f.overlay_cairo_surface(surf, 0, 0)
end

View File

@ -26,8 +26,9 @@
#include "cairo_wrap.h"
#include <math.h>
#include <windows.h>
//#include <windows.h>
#include <omp.h>
#include <stdint.h>
#include "raster_ops.h"
#include "../lua51/src/lauxlib.h"
@ -177,6 +178,17 @@ static inline void ImgToSurf(lua_State *L, int idx, const Img &img)
cairo_surface_mark_dirty(surf);
}*/
static inline cairo_surface_t *CheckSurface(lua_State *L, int idx)
{
LuaCairoSurface *surfobj = LuaCairoSurface::GetObjPointer(L, idx);
cairo_surface_t *surf = surfobj->GetSurface();
if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_IMAGE) {
lua_pushliteral(L, "Object for raster operation is not an image surface. Video frames are not accepted.");
lua_error(L);
}
return surf;
}
static inline double NormalDist(double sigma, double x)
{
if (sigma <= 0 && x == 0) return 1;
@ -247,10 +259,6 @@ void SeparableFilterY(unsigned char *src, unsigned char *dst, int width, int hei
void ApplySeparableFilter(lua_State *L, cairo_surface_t *surf, int *kernel, int kernel_size, int divisor)
{
// Get surface properties
if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_IMAGE) {
lua_pushliteral(L, "Object for raster operation is not an image surface. Video frames are not accepted.");
lua_error(L);
}
cairo_surface_flush(surf);
int width = cairo_image_surface_get_width(surf);
int height = cairo_image_surface_get_height(surf);
@ -293,7 +301,7 @@ void ApplySeparableFilter(lua_State *L, cairo_surface_t *surf, int *kernel, int
static int gaussian_blur(lua_State *L)
{
// Get arguments
LuaCairoSurface *surfobj = LuaCairoSurface::GetObjPointer(L, 1);
cairo_surface_t *surf = CheckSurface(L, 1);
double sigma = luaL_checknumber(L, 2);
// Generate gaussian kernel
@ -308,16 +316,8 @@ static int gaussian_blur(lua_State *L)
kernel[x] = val;
kernel[kernel_size - x - 1] = val;
}
/*while (ksum < 255) {
kernel[ksum%kernel_size]++;
ksum++;
}
while (ksum > 255) {
kernel[ksum%kernel_size]--;
ksum--;
}*/
ApplySeparableFilter(L, surfobj->GetSurface(), kernel, kernel_size, ksum);
ApplySeparableFilter(L, surf, kernel, kernel_size, ksum);
delete[] kernel;
@ -328,7 +328,7 @@ static int gaussian_blur(lua_State *L)
// n tap box blur
static int box_blur(lua_State *L)
{
LuaCairoSurface *surfobj = LuaCairoSurface::GetObjPointer(L, 1);
cairo_surface_t *surf = CheckSurface(L, 1);
int width = luaL_checkint(L, 2);
if (width <= 0) { luaL_error(L, "Width of box kernel for raster.box must be larger than zero, specified to %d.", width); return 0; }
int applications = luaL_optint(L, 3, 1);
@ -336,7 +336,7 @@ static int box_blur(lua_State *L)
for (int i = 0; i < width; i++)
kernel[i] = 1;
while (applications-- > 0)
ApplySeparableFilter(L, surfobj->GetSurface(), kernel, width, width);
ApplySeparableFilter(L, surf, kernel, width, width);
delete[] kernel;
return 0;
}
@ -344,32 +344,62 @@ static int box_blur(lua_State *L)
static int invert_image(lua_State *L)
{
LuaCairoSurface *surfobj = LuaCairoSurface::GetObjPointer(L, 1);
cairo_surface_t *surf = surfobj->GetSurface();
if (cairo_surface_get_type(surf) != CAIRO_SURFACE_TYPE_IMAGE) {
lua_pushliteral(L, "Object for raster operation is not an image surface. Video frames are not accepted.");
lua_error(L);
}
cairo_surface_t *surf = CheckSurface(L, 1);
cairo_surface_flush(surf);
size_t width = (size_t)cairo_image_surface_get_width(surf);
size_t height = (size_t)cairo_image_surface_get_height(surf);
int width = cairo_image_surface_get_width(surf);
int height = cairo_image_surface_get_height(surf);
ptrdiff_t stride = (ptrdiff_t)cairo_image_surface_get_stride(surf);
unsigned char *data = cairo_image_surface_get_data(surf);
cairo_format_t format = cairo_image_surface_get_format(surf);
size_t datawidth = stride;
if (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24)
datawidth = width*4;
else if (format == CAIRO_FORMAT_A8)
datawidth = width;
else if (format == CAIRO_FORMAT_A1)
datawidth = (width+31)/8 & ~4; // make sure this is rounded up to nearest four bytes
// This could be faster if done over 32 bit quantities
for (size_t y = 0; y < height; y++) {
unsigned char *line = data + y*stride;
for (size_t x = 0; x < datawidth; x++, line++)
*line = ~ *line;
// ARGB32 and RGB24 must be treated differently due to the premultipled alpha in ARGB32
// Also the alpha in ARGB32 is not inverted, only the colour channels
if (format == CAIRO_FORMAT_ARGB32) {
#pragma omp parallel for
for (int y = 0; y < height; y++) {
uint32_t *line = (uint32_t*)(data + y*stride);
for (int x = 0; x < width; x++, line++) {
// The R, G and B channels are in range 0..a, and inverting means calculating
// max-current, so inversion here is a-c for each channel.
unsigned char a = (*line & 0xff000000) >> 24;
unsigned char r = (*line & 0x00ff0000) >> 16;
unsigned char g = (*line & 0x0000ff00) >> 8;
unsigned char b = *line & 0x000000ff;
*line = (a<<24) | ((a-r)<<16) | ((a-g)<<8) | (a-b);
}
}
}
else if (format == CAIRO_FORMAT_RGB24) {
#pragma omp parallel for
for (int y = 0; y < height; y++) {
uint32_t *line = (uint32_t*)(data + y*stride);
for (int x = 0; x < width; x++, line++) {
// Invert R, G and B channels by XOR'ing them with 0xff each.
*line = *line ^ 0x00ffffff;
}
}
}
else if (format == CAIRO_FORMAT_A8) {
#pragma omp parallel for
for (int y = 0; y < height; y++) {
unsigned char *line = data + y*stride;
for (int x = 0; x < width; x++, line++) {
*line = ~ *line;
}
}
}
else if (format == CAIRO_FORMAT_A1) {
int lwidth = (width + 31) / 32; // "long-width", width in 32 bit longints, rounded up
#pragma omp parallel for
for (int y = 0; y < height; y++) {
// Pixels are stored packed into 32 bit quantities
uint32_t *line = (uint32_t*)(data + y*stride);
for (int x = 0; x < lwidth; x++, line++) {
// Yes this means we might end up inverting too many bits.. hopefully won't happen.
// (And even then, who would seriously use A1 surfaces?)
*line = ~ *line;
}
}
}
cairo_surface_mark_dirty(surf);
@ -381,8 +411,8 @@ static int invert_image(lua_State *L)
// Registration
static luaL_Reg rasterlib[] = {
{"gaussian_blur", gaussian_blur},
{"box", box_blur},
{"gaussian_blur", gaussian_blur}, {"box_blur", box_blur},
{"invert", invert_image},
{NULL, NULL}
};

View File

@ -333,38 +333,35 @@ badtable:
if (sformat == CAIRO_FORMAT_ARGB32) {
// This has pre-multipled alpha
int fy = ypos; // frame y
int sy = 0; // source y
if (fy < 0) fy = 0, sy = -ypos;
int slines_to_compose = sheight-sy, flines_to_compose = fheight;
if (fy < 0) fy = 0, sdata -= ypos, sheight -= ypos;
int slines_to_compose = sheight, flines_to_compose = fheight;
int lines_to_compose = (slines_to_compose<flines_to_compose)?slines_to_compose:flines_to_compose;
#pragma omp parallel for
for (int composition_line = 0; composition_line < lines_to_compose; composition_line++) {
uint32_t *sline = (uint32_t*)(sdata + sy*sstride);
uint32_t *sline = (uint32_t*)(sdata + composition_line*sstride);
int fx = xpos;
int sx = 0;
if (fx < 0) fx = 0, sx = -xpos;
for ( ; sx < swidth && fx < fwidth; fx++, sx++) {
RGBPixel pix = (*ud)->GetPixel(fx, fy);
RGBPixel pix = (*ud)->GetPixel(fx, fy+composition_line);
unsigned char a = 0xff - ((sline[sx] & 0xff000000) >> 24);
pix.r = ((sline[sx] & 0x00ff0000) >> 16) + a*pix.r/255;
pix.g = ((sline[sx] & 0x0000ff00) >> 8) + a*pix.g/255;
pix.b = (sline[sx] & 0x000000ff) + a*pix.b/255;
(*ud)->SetPixel(fx, fy, pix);
(*ud)->SetPixel(fx, fy+composition_line, pix);
}
fy++, sy++;
}
}
else if (sformat == CAIRO_FORMAT_RGB24) {
// Assume opaque alpha for all pixels
int fy = ypos; // frame y
int sy = 0; // source y
if (fy < 0) fy = 0, sy = -ypos;
int slines_to_compose = sheight-sy, flines_to_compose = fheight;
if (fy < 0) fy = 0, sdata -= ypos, sheight -= ypos;
int slines_to_compose = sheight, flines_to_compose = fheight;
int lines_to_compose = (slines_to_compose<flines_to_compose)?slines_to_compose:flines_to_compose;
#pragma omp parallel for
for (int composition_line = 0; composition_line < lines_to_compose; composition_line++) {
uint32_t *sline = (uint32_t*)(sdata + sy*sstride);
uint32_t *sline = (uint32_t*)(sdata + composition_line*sstride);
int fx = xpos;
int sx = 0;
if (fx < 0) fx = 0, sx = -xpos;
@ -373,7 +370,7 @@ badtable:
(sline[sx] & 0x00ff0000) >> 16,
(sline[sx] & 0x0000ff00) >> 8,
sline[sx] & 0x000000ff);
(*ud)->SetPixel(fx, fy, pix);
(*ud)->SetPixel(fx, fy+composition_line, pix);
}
}
}