/* * Compiz motion blur effect plugin * * mblur.c * * Copyright : (C) 2007 by Dennis Kasprzyk * E-mail : onestone@beryl-project.org * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include "mblur_options.h" #define GET_MBLUR_DISPLAY(d) \ ((MblurDisplay *) (d)->base.privates[displayPrivateIndex].ptr) #define MBLUR_DISPLAY(d) \ MblurDisplay *md = GET_MBLUR_DISPLAY (d) #define GET_MBLUR_SCREEN(s, md) \ ((MblurScreen *) (s)->base.privates[(md)->screenPrivateIndex].ptr) #define MBLUR_SCREEN(s) \ MblurScreen *ms = GET_MBLUR_SCREEN (s, GET_MBLUR_DISPLAY (s->display)) static int displayPrivateIndex = 0; typedef struct _MblurDisplay { int screenPrivateIndex; } MblurDisplay; typedef struct _MblurScreen { /* functions that we will intercept */ PreparePaintScreenProc preparePaintScreen; PaintScreenProc paintScreen; PaintTransformedOutputProc paintTransformedOutput; Bool active; Bool update; /* is an update of the motion blut texture needed */ float alpha; /* motion blur blending value */ float timer; /* motion blur fadeout time */ Bool activated; GLuint texture; } MblurScreen; /* activate/deactivate motion blur */ static Bool mblurToggle (CompDisplay *d, CompAction *ac, CompActionState state, CompOption *option, int nOption) { CompScreen *s; s = findScreenAtDisplay (d, getIntOptionNamed (option, nOption, "root", 0)); if (s) { MBLUR_SCREEN (s); ms->activated = !ms->activated; } return FALSE; } static void mblurPreparePaintScreen (CompScreen * s, int msec) { MBLUR_SCREEN (s); ms->active |= ms->activated; /* fade motion blur out if no longer active */ if (ms->activated) { ms->timer = 500; } else { ms->timer -= msec; } // calculate motion blur strength dependent on framerate float val = 101 - MIN (100, MAX (1, msec) ); float a_val = mblurGetStrength (s) / 20.0; a_val = a_val * a_val; a_val /= 100.0; ms->alpha = 1.0 - pow (a_val, 1.0 / val); if (ms->active && ms->timer <= 0) damageScreen (s); if (ms->timer <= 0) { ms->active = FALSE; } if (ms->update && ms->active) damageScreen (s); UNWRAP (ms, s, preparePaintScreen); (*s->preparePaintScreen) (s, msec); WRAP (ms, s, preparePaintScreen, mblurPreparePaintScreen); } static void mblurPaintScreen (CompScreen *s, CompOutput *outputs, int numOutput, unsigned int mask) { MBLUR_SCREEN (s); if (!ms->active) ms->update = TRUE; UNWRAP (ms, s, paintScreen); (*s->paintScreen) (s, outputs, numOutput, mask); WRAP (ms, s, paintScreen, mblurPaintScreen); Bool enable_scissor = FALSE; if (ms->active && glIsEnabled (GL_SCISSOR_TEST) ) { glDisable (GL_SCISSOR_TEST); enable_scissor = TRUE; } if (ms->active && mblurGetMode (s) == ModeTextureCopy) { float tx, ty; GLuint target; if (s->textureNonPowerOfTwo || (POWER_OF_TWO (s->width) && POWER_OF_TWO (s->height) ) ) { target = GL_TEXTURE_2D; tx = 1.0f / s->width; ty = 1.0f / s->height; } else { target = GL_TEXTURE_RECTANGLE_NV; tx = 1; ty = 1; } if (!ms->texture) { glGenTextures (1, &ms->texture); glBindTexture (target, ms->texture); glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture (target, 0); } // blend motion blur texture to screen glPushAttrib (GL_COLOR_BUFFER_BIT | GL_TEXTURE_BIT | GL_VIEWPORT_BIT); glPushMatrix (); glLoadIdentity (); glViewport (0, 0, s->width, s->height); glTranslatef (-0.5f, -0.5f, -DEFAULT_Z_CAMERA); glScalef (1.0f / s->width, -1.0f / s->height, 1.0f); glTranslatef (0.0f, -s->height, 0.0f); glBindTexture (target, ms->texture); glEnable (target); if (!ms->update) { glEnable (GL_BLEND); glBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA); ms->alpha = (ms->timer / 500.0) * ms->alpha + (1.0 - (ms->timer / 500.0) ) * 0.5; glColor4f (1, 1, 1, ms->alpha); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBegin (GL_QUADS); glTexCoord2f (0, s->height * ty); glVertex2f (0, 0); glTexCoord2f (0, 0); glVertex2f (0, s->height); glTexCoord2f (s->width * tx, 0); glVertex2f (s->width, s->height); glTexCoord2f (s->width * tx, s->height * ty); glVertex2f (s->width, 0); glEnd (); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); // copy new screen to motion blur texture glCopyTexSubImage2D (target, 0, 0, 0, 0, 0, s->width, s->height); } else { glCopyTexImage2D (target, 0, GL_RGB, 0, 0, s->width, s->height, 0); } glBindTexture (target, 0); glDisable (target); glPopMatrix (); glPopAttrib (); ms->update = FALSE; damageScreen (s); } if (ms->active && mblurGetMode (s) == ModeAccumulationBuffer) { // create motion blur effect using accumulation buffer ms->alpha = (ms->timer / 500.0) * ms->alpha + (1.0 - (ms->timer / 500.0) ) * 0.5; if (ms->update) { glAccum (GL_LOAD, 1.0); } else { glAccum (GL_MULT, 1.0 - ms->alpha); glAccum (GL_ACCUM, ms->alpha); glAccum (GL_RETURN, 1.0); } ms->update = FALSE; damageScreen (s); } if (enable_scissor) glEnable (GL_SCISSOR_TEST); } static void mblurPaintTransformedOutput (CompScreen *s, const ScreenPaintAttrib *sa, const CompTransform *transform, Region region, CompOutput *output, unsigned int mask) { MBLUR_SCREEN (s); if (mblurGetOnTransformedScreen (s) && (mask & PAINT_SCREEN_TRANSFORMED_MASK) ) { ms->active = TRUE; ms->timer = 500; } UNWRAP (ms, s, paintTransformedOutput); (*s->paintTransformedOutput) (s, sa, transform, region, output, mask); WRAP (ms, s, paintTransformedOutput, mblurPaintTransformedOutput); } static Bool mblurInit (CompPlugin *p) { displayPrivateIndex = allocateDisplayPrivateIndex (); if (displayPrivateIndex < 0) return FALSE; return TRUE; } static void mblurFini (CompPlugin *p) { if (displayPrivateIndex >= 0) freeDisplayPrivateIndex (displayPrivateIndex); } static Bool mblurInitDisplay (CompPlugin *p, CompDisplay *d) { MblurDisplay *md; if (!checkPluginABI ("core", CORE_ABIVERSION)) return FALSE; /* Generate a blur display */ md = (MblurDisplay *) calloc (1, sizeof (MblurDisplay)); if (!md) return FALSE; /* Allocate a private index */ md->screenPrivateIndex = allocateScreenPrivateIndex (d); /* Check if its valid */ if (md->screenPrivateIndex < 0) { /* It's invalid so free memory and return */ free (md); return FALSE; } /* Record the display */ d->base.privates[displayPrivateIndex].ptr = md; mblurSetInitiateKeyInitiate (d, mblurToggle); return TRUE; } static void mblurFiniDisplay (CompPlugin *p, CompDisplay *d) { MBLUR_DISPLAY (d); /*Free the private index */ freeScreenPrivateIndex (d, md->screenPrivateIndex); /*Free the pointer */ free (md); } static Bool mblurInitScreen (CompPlugin *p, CompScreen *s) { MBLUR_DISPLAY (s->display); /* Create a blur screen */ MblurScreen *ms = (MblurScreen *) calloc (1, sizeof (MblurScreen) ); s->base.privates[md->screenPrivateIndex].ptr = ms; ms->activated = FALSE; ms->update = TRUE; ms->texture = 0; /* Take over the window draw function */ WRAP (ms, s, paintScreen, mblurPaintScreen); WRAP (ms, s, preparePaintScreen, mblurPreparePaintScreen); WRAP (ms, s, paintTransformedOutput, mblurPaintTransformedOutput); damageScreen (s); return TRUE; } static void mblurFiniScreen (CompPlugin *p, CompScreen *s) { MBLUR_SCREEN (s); if (ms->texture) glDeleteTextures (1, &ms->texture); /* restore the original function */ UNWRAP (ms, s, paintScreen); UNWRAP (ms, s, preparePaintScreen); UNWRAP (ms, s, paintTransformedOutput); /* free the screen pointer */ free (ms); } static CompBool mblurInitObject (CompPlugin *p, CompObject *o) { static InitPluginObjectProc dispTab[] = { (InitPluginObjectProc) 0, (InitPluginObjectProc) mblurInitDisplay, (InitPluginObjectProc) mblurInitScreen }; RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o)); } static void mblurFiniObject (CompPlugin *p, CompObject *o) { static FiniPluginObjectProc dispTab[] = { (FiniPluginObjectProc) 0, (FiniPluginObjectProc) mblurFiniDisplay, (FiniPluginObjectProc) mblurFiniScreen }; DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o)); } CompPluginVTable mblurVTable = { "mblur", 0, mblurInit, mblurFini, mblurInitObject, mblurFiniObject, 0, 0 }; CompPluginVTable * getCompPluginInfo (void) { return &mblurVTable; }