diff options
Diffstat (limited to 'plugins/blur')
-rw-r--r-- | plugins/blur/CMakeLists.txt | 16 | ||||
-rw-r--r-- | plugins/blur/blur.xml.in | 112 | ||||
-rw-r--r-- | plugins/blur/src/blur.cpp | 2335 | ||||
-rw-r--r-- | plugins/blur/src/blur.h | 241 |
4 files changed, 2704 insertions, 0 deletions
diff --git a/plugins/blur/CMakeLists.txt b/plugins/blur/CMakeLists.txt new file mode 100644 index 0000000..d625cdf --- /dev/null +++ b/plugins/blur/CMakeLists.txt @@ -0,0 +1,16 @@ +find_package (Compiz REQUIRED) + +include (CompizPlugin) + +find_package (OpenGL) + +if (OPENGL_GLU_FOUND) + compiz_plugin(blur PLUGINDEPS composite opengl LIBRARIES decoration ${OPENGL_glu_LIBRARY} INCDIRS ${OPENGL_INCLUDE_DIR}) + + if (COMPIZ_BUILD_WITH_RPATH) + set_target_properties ( + blur PROPERTIES + INSTALL_RPATH "${COMPIZ_LIBDIR}" + ) + endif (COMPIZ_BUILD_WITH_RPATH) +endif () diff --git a/plugins/blur/blur.xml.in b/plugins/blur/blur.xml.in new file mode 100644 index 0000000..b8af0b0 --- /dev/null +++ b/plugins/blur/blur.xml.in @@ -0,0 +1,112 @@ +<compiz> + <plugin name="blur" useBcop="true"> + <_short>Blur Windows</_short> + <_long>Blur windows</_long> + <category>Effects</category> + <feature>blur</feature> + <deps> + <relation type="before"> + <plugin>video</plugin> + </relation> + <relation type="after"> + <plugin>decor</plugin> + </relation> + <requirement> + <plugin>opengl</plugin> + </requirement> + </deps> + <options> + <option name="pulse" type="bell"> + <_short>Pulse</_short> + <_long>Pulse effect</_long> + </option> + <option name="blur_speed" type="float"> + <_short>Blur Speed</_short> + <_long>Window blur speed</_long> + <default>3.5</default> + <min>0.1</min> + <max>10.0</max> + <precision>0.1</precision> + </option> + <option name="focus_blur_match" type="match"> + <_short>Focus blur windows</_short> + <_long>Windows that should be affected by focus blur</_long> + <default>toolbar | menu | utility | normal | dialog | modaldialog</default> + </option> + <option name="focus_blur" type="bool"> + <_short>Focus Blur</_short> + <_long>Blur windows that doesn't have focus</_long> + <default>false</default> + </option> + <option name="alpha_blur_match" type="match"> + <_short>Alpha blur windows</_short> + <_long>Windows that should be use alpha blur by default</_long> + <default></default> + </option> + <option name="alpha_blur" type="bool"> + <_short>Alpha Blur</_short> + <_long>Blur behind translucent parts of windows</_long> + <default>true</default> + </option> + <option name="filter" type="int"> + <_short>Blur Filter</_short> + <_long>Filter method used for blurring</_long> + <default>0</default> + <min>0</min> + <max>2</max> + <desc> + <value>0</value> + <_name>4xBilinear</_name> + </desc> + <desc> + <value>1</value> + <_name>Gaussian</_name> + </desc> + <desc> + <value>2</value> + <_name>Mipmap</_name> + </desc> + </option> + <option name="gaussian_radius" type="int"> + <_short>Gaussian Radius</_short> + <_long>Gaussian radius</_long> + <default>3</default> + <min>1</min> + <max>15</max> + </option> + <option name="gaussian_strength" type="float"> + <_short>Gaussian Strength</_short> + <_long>Gaussian strength</_long> + <default>1.0</default> + <min>0.0</min> + <max>1.0</max> + <precision>0.1</precision> + </option> + <option name="mipmap_lod" type="float"> + <_short>Mipmap LOD</_short> + <_long>Mipmap level-of-detail</_long> + <default>2.5</default> + <min>0.1</min> + <max>5.0</max> + <precision>0.1</precision> + </option> + <option name="saturation" type="int"> + <_short>Blur Saturation</_short> + <_long>Blur saturation</_long> + <default>100</default> + <min>0</min> + <max>100</max> + </option> + <option name="occlusion" type="bool"> + <_short>Blur Occlusion</_short> + <_long>Disable blurring of screen regions obscured by other windows.</_long> + <default>true</default> + </option> + <option name="independent_tex" type="bool"> + <_short>Independent texture fetch</_short> + <_long>Use the available texture units to do as many as possible independent texture fetches.</_long> + <default>false</default> + </option> + </options> + </plugin> +</compiz> diff --git a/plugins/blur/src/blur.cpp b/plugins/blur/src/blur.cpp new file mode 100644 index 0000000..555b7d0 --- /dev/null +++ b/plugins/blur/src/blur.cpp @@ -0,0 +1,2335 @@ +/* + * Copyright © 2007 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <blur.h> + +COMPIZ_PLUGIN_20090315 (blur, BlurPluginVTable) + +/* pascal triangle based kernel generator */ +static int +blurCreateGaussianLinearKernel (int radius, + float strength, + float *amp, + float *pos, + int *optSize) +{ + float factor = 0.5f + (strength / 2.0f); + float buffer1[BLUR_GAUSSIAN_RADIUS_MAX * 3]; + float buffer2[BLUR_GAUSSIAN_RADIUS_MAX * 3]; + float *ar1 = buffer1; + float *ar2 = buffer2; + float *tmp; + float sum = 0; + int size = (radius * 2) + 1; + int mySize = ceil (radius / 2.0f); + int i, j; + + ar1[0] = 1.0; + ar1[1] = 1.0; + + for (i = 3; i <= size; i++) + { + ar2[0] = 1; + + for (j = 1; j < i - 1; j++) + ar2[j] = (ar1[j - 1] + ar1[j]) * factor; + + ar2[i - 1] = 1; + + tmp = ar1; + ar1 = ar2; + ar2 = tmp; + } + + /* normalize */ + for (i = 0; i < size; i++) + sum += ar1[i]; + + if (sum != 0.0f) + sum = 1.0f / sum; + + for (i = 0; i < size; i++) + ar1[i] *= sum; + + i = 0; + j = 0; + + if (radius & 1) + { + pos[i] = radius; + amp[i] = ar1[i]; + i = 1; + j = 1; + } + + for (; i < mySize; i++) + { + pos[i] = radius - j; + pos[i] -= ar1[j + 1] / (ar1[j] + ar1[j + 1]); + amp[i] = ar1[j] + ar1[j + 1]; + + j += 2; + } + + pos[mySize] = 0.0; + amp[mySize] = ar1[radius]; + + *optSize = mySize; + + return radius; +} + +void +BlurScreen::updateFilterRadius () +{ + switch (optionGetFilter ()) { + case BlurOptions::Filter4xbilinear: + filterRadius = 2; + break; + case BlurOptions::FilterGaussian: { + int radius = optionGetGaussianRadius (); + float strength = optionGetGaussianStrength (); + + blurCreateGaussianLinearKernel (radius, strength, amp, pos, + &numTexop); + + filterRadius = radius; + } break; + case BlurOptions::FilterMipmap: { + float lod = optionGetMipmapLod (); + + filterRadius = powf (2.0f, ceilf (lod)); + } break; + } +} + + +void +BlurScreen::blurReset () +{ + updateFilterRadius (); + + foreach (BlurFunction &bf, srcBlurFunctions) + GLFragment::destroyFragmentFunction (bf.id); + srcBlurFunctions.clear (); + foreach (BlurFunction &bf, dstBlurFunctions) + GLFragment::destroyFragmentFunction (bf.id); + dstBlurFunctions.clear (); + + width = height = 0; + + if (program) + { + GL::deletePrograms (1, &program); + program = 0; + } +} + +static CompRegion +regionFromBoxes (std::vector<BlurBox> boxes, + int width, + int height) +{ + CompRegion region; + int x1, x2, y1, y2; + + foreach (BlurBox &box, boxes) + { + decor_apply_gravity (box.p1.gravity, box.p1.x, box.p1.y, + width, height, + &x1, &y1); + + + decor_apply_gravity (box.p2.gravity, box.p2.x, box.p2.y, + width, height, + &x2, &y2); + + if (x2 > x1 && y2 > y1) + region += CompRect (x1, y1, x2 - x1, y2 - y1); + } + + return region; +} + +void +BlurWindow::updateRegion () +{ + CompRegion region; + + if (state[BLUR_STATE_DECOR].threshold) + { + region += CompRect (-window->output ().left, + -window->output ().top, + window->width () + window->output ().right, + window->height () + window->output ().bottom); + + region -= CompRect (0, 0, window->width (), window->height ()); + + state[BLUR_STATE_DECOR].clipped = false; + + if (!state[BLUR_STATE_DECOR].box.empty ()) + { + CompRegion q = regionFromBoxes (state[BLUR_STATE_DECOR].box, + window->width (), + window->height ()); + if (!q.isEmpty ()) + { + q &= region; + if (q != region) + { + region = q; + state[BLUR_STATE_DECOR].clipped = true; + } + } + } + } + + if (state[BLUR_STATE_CLIENT].threshold) + { + CompRegion r (0, 0, window->width (), window->height ()); + + state[BLUR_STATE_CLIENT].clipped = false; + + if (!state[BLUR_STATE_CLIENT].box.empty ()) + { + CompRegion q = regionFromBoxes (state[BLUR_STATE_CLIENT].box, + window->width (), + window->height ()); + if (!q.isEmpty ()) + { + q &= r; + + if (q != r) + state[BLUR_STATE_CLIENT].clipped = true; + + region += q; + } + } + else + { + region += r; + } + } + + this->region = region; + if (!region.isEmpty ()) + this->region.translate (window->x (), window->y ()); +} + +void +BlurWindow::setBlur (int state, + int threshold, + std::vector<BlurBox> box) +{ + + this->state[state].threshold = threshold; + this->state[state].box = box; + + updateRegion (); + + cWindow->addDamage (); +} + +void +BlurWindow::updateAlphaMatch () +{ + if (!propSet[BLUR_STATE_CLIENT]) + { + CompMatch *match; + + match = &bScreen->optionGetAlphaBlurMatch (); + if (match->evaluate (window)) + { + if (!state[BLUR_STATE_CLIENT].threshold) + setBlur (BLUR_STATE_CLIENT, 4, std::vector<BlurBox> ()); + } + else + { + if (state[BLUR_STATE_CLIENT].threshold) + setBlur (BLUR_STATE_CLIENT, 0, std::vector<BlurBox> ()); + } + } +} + +void +BlurWindow::updateMatch () +{ + CompMatch *match; + bool focus; + + updateAlphaMatch (); + + match = &bScreen->optionGetFocusBlurMatch (); + + focus = GL::fragmentProgram && match->evaluate (window); + if (focus != focusBlur) + { + focusBlur = focus; + cWindow->addDamage (); + } +} + + + +void +BlurWindow::update (int state) +{ + Atom actual; + int result, format; + unsigned long n, left; + unsigned char *propData; + int threshold = 0; + std::vector<BlurBox> boxes; + + result = XGetWindowProperty (screen->dpy (), window->id (), + bScreen->blurAtom[state], 0L, 8192L, false, + XA_INTEGER, &actual, &format, + &n, &left, &propData); + + if (result == Success && n && propData) + { + propSet[state] = true; + + if (n >= 2) + { + long *data = (long *) propData; + BlurBox box; + + threshold = data[0]; + + if ((n - 2) / 6) + { + unsigned int i; + + data += 2; + + for (i = 0; i < (n - 2) / 6; i++) + { + box.p1.gravity = *data++; + box.p1.x = *data++; + box.p1.y = *data++; + box.p2.gravity = *data++; + box.p2.x = *data++; + box.p2.y = *data++; + + boxes.push_back (box); + } + + } + } + + XFree (propData); + } + else + { + propSet[state] = false; + } + + setBlur (state, threshold, boxes); + + updateAlphaMatch (); +} + +void +BlurScreen::preparePaint (int msSinceLastPaint) +{ + if (moreBlur) + { + int steps; + bool focus = optionGetFocusBlur (); + bool focusBlur; + + steps = (msSinceLastPaint * 0xffff) / blurTime; + if (steps < 12) + steps = 12; + + moreBlur = false; + + foreach (CompWindow *w, screen->windows ()) + { + BLUR_WINDOW (w); + + focusBlur = bw->focusBlur && focus; + + if (!bw->pulse && + (!focusBlur || w->id () == screen->activeWindow ())) + { + if (bw->blur) + { + bw->blur -= steps; + if (bw->blur > 0) + moreBlur = true; + else + bw->blur = 0; + } + } + else + { + if (bw->blur < 0xffff) + { + if (bw->pulse) + { + bw->blur += steps * 2; + + if (bw->blur >= 0xffff) + { + bw->blur = 0xffff - 1; + bw->pulse = false; + } + + moreBlur = true; + } + else + { + bw->blur += steps; + if (bw->blur < 0xffff) + moreBlur = true; + else + bw->blur = 0xffff; + } + } + } + } + } + + cScreen->preparePaint (msSinceLastPaint); + + if (cScreen->damageMask () & COMPOSITE_SCREEN_DAMAGE_REGION_MASK) + { + /* walk from bottom to top and expand damage */ + if (alphaBlur) + { + int x1, y1, x2, y2; + int count = 0; + CompRegion damage (cScreen->currentDamage ()); + + foreach (CompWindow *w, screen->windows ()) + { + BLUR_WINDOW (w); + + if (!w->isViewable () || !CompositeWindow::get (w)->damaged ()) + continue; + + if (!bw->region.isEmpty ()) + { + CompRect r = bw->region.boundingRect (); + CompRect d = damage.boundingRect (); + x1 = r.x1 () - filterRadius; + y1 = r.y1 () - filterRadius; + x2 = r.x2 () + filterRadius; + y2 = r.y2 () + filterRadius; + + if (x1 < d.x2 () && + y1 < d.y2 () && + x2 > d.x1 () && + y2 > d.y1 ()) + { + damage.shrink (-filterRadius, -filterRadius); + count++; + } + } + } + + if (count) + cScreen->damageRegion (damage); + + this->count = count; + } + } +} + +bool +BlurScreen::glPaintOutput (const GLScreenPaintAttrib &sAttrib, + const GLMatrix &transform, const CompRegion ®ion, + CompOutput *output, unsigned int mask) +{ + bool status; + + if (alphaBlur) + { + stencilBox = region.boundingRect (); + this->region = region; + + if (mask & PAINT_SCREEN_REGION_MASK) + { + /* we need to redraw more than the screen region being updated */ + if (count) + { + this->region.shrink (-filterRadius * 2, -filterRadius * 2); + + this->region &= screen->region (); + } + } + } + + if (!blurOcclusion) + { + occlusion = CompRegion (); + + foreach (CompWindow *w, screen->windows ()) + BlurWindow::get (w)->clip = CompRegion (); + } + + this->output = output; + + if (alphaBlur) + status = gScreen->glPaintOutput (sAttrib, transform, this->region, output, mask); + else + status = gScreen->glPaintOutput (sAttrib, transform, region, output, mask); + + return status; +} + +void +BlurScreen::glPaintTransformedOutput (const GLScreenPaintAttrib &sAttrib, + const GLMatrix &transform, + const CompRegion ®ion, + CompOutput *output, unsigned int mask) +{ + if (!blurOcclusion) + { + occlusion = CompRegion (); + + foreach (CompWindow *w, screen->windows ()) + BlurWindow::get (w)->clip = CompRegion (); + } + + gScreen->glPaintTransformedOutput (sAttrib, transform, region, output, mask); +} + +void +BlurScreen::donePaint () +{ + if (moreBlur) + { + foreach (CompWindow *w, screen->windows ()) + { + BLUR_WINDOW (w); + + if (bw->blur > 0 && bw->blur < 0xffff) + bw->cWindow->addDamage (); + } + } + + cScreen->donePaint (); +} + +bool +BlurWindow::glPaint (const GLWindowPaintAttrib &attrib, + const GLMatrix &transform, + const CompRegion ®ion, unsigned int mask) +{ + + bool status = gWindow->glPaint (attrib, transform, region, mask); + + if (!bScreen->blurOcclusion && + (mask & PAINT_WINDOW_OCCLUSION_DETECTION_MASK)) + { + clip = bScreen->occlusion; + + if (!(gWindow->lastMask () & PAINT_WINDOW_NO_CORE_INSTANCE_MASK) && + !(gWindow->lastMask () & PAINT_WINDOW_TRANSFORMED_MASK) && + !this->region.isEmpty ()) + bScreen->occlusion += this->region; + } + + return status; +} + +GLFragment::FunctionId +BlurScreen::getSrcBlurFragmentFunction (GLTexture *texture, + int param) +{ + GLFragment::FunctionData data; + BlurFunction function; + int target; + + if (texture->target () == GL_TEXTURE_2D) + target = COMP_FETCH_TARGET_2D; + else + target = COMP_FETCH_TARGET_RECT; + + foreach (BlurFunction &bf, srcBlurFunctions) + if (bf.param == param && bf.target == target) + return bf.id; + + if (data.status ()) + { + static const char *temp[] = { "offset0", "offset1", "sum" }; + unsigned int i; + + for (i = 0; i < sizeof (temp) / sizeof (temp[0]); i++) + data.addTempHeaderOp (temp[i]); + + data.addDataOp ( + "MUL offset0, program.env[%d].xyzw, { 1.0, 1.0, 0.0, 0.0 };" + "MUL offset1, program.env[%d].zwww, { 1.0, 1.0, 0.0, 0.0 };", + param, param); + + + switch (optionGetFilter ()) { + case BlurOptions::Filter4xbilinear: + default: + data.addFetchOp ("output", "offset0", target); + data.addDataOp ("MUL sum, output, 0.25;"); + data.addFetchOp ("output", "-offset0", target); + data.addDataOp ("MAD sum, output, 0.25, sum;"); + data.addFetchOp ("output", "offset1", target); + data.addDataOp ("MAD sum, output, 0.25, sum;"); + data.addFetchOp ("output", "-offset1", target); + data.addDataOp ("MAD output, output, 0.25, sum;"); + break; + } + + if (!data.status ()) + return 0; + + function.id = data.createFragmentFunction ("blur"); + function.target = target; + function.param = param; + function.unit = 0; + + srcBlurFunctions.push_back (function); + + return function.id; + } + + return 0; +} + +GLFragment::FunctionId +BlurScreen::getDstBlurFragmentFunction (GLTexture *texture, + int param, + int unit, + int numITC, + int startTC) +{ + BlurFunction function; + GLFragment::FunctionData data; + int target; + char *targetString; + + if (texture->target () == GL_TEXTURE_2D) + { + target = COMP_FETCH_TARGET_2D; + targetString = (char *) "2D"; + } + else + { + target = COMP_FETCH_TARGET_RECT; + targetString = (char *) "RECT"; + } + + foreach (BlurFunction &function, dstBlurFunctions) + if (function.param == param && + function.target == target && + function.unit == unit && + function.numITC == numITC && + function.startTC == startTC) + return function.id; + + if (data.status ()) + { + static const char *temp[] = { "fCoord", "mask", "sum", "dst" }; + int i, j; + char str[1024]; + int saturation = optionGetSaturation (); + int numIndirect; + int numIndirectOp; + int base, end, ITCbase; + + for (i = 0; (unsigned int) i < sizeof (temp) / sizeof (temp[0]); i++) + data.addTempHeaderOp (temp[i]); + + if (saturation < 100) + data.addTempHeaderOp ("sat"); + + switch (optionGetFilter ()) { + case BlurOptions::Filter4xbilinear: { + static const char *filterTemp[] = { + "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3" + }; + + for (i = 0; + (unsigned int) i < sizeof (filterTemp) / sizeof (filterTemp[0]); + i++) + data.addTempHeaderOp (filterTemp[i]); + + data.addFetchOp ("output", NULL, target); + data.addColorOp ("output", "output"); + + data.addDataOp ( + "MUL fCoord, fragment.position, program.env[%d];", + param); + + + data.addDataOp ( + "ADD t0, fCoord, program.env[%d];" + "TEX s0, t0, texture[%d], %s;" + + "SUB t1, fCoord, program.env[%d];" + "TEX s1, t1, texture[%d], %s;" + + "MAD t2, program.env[%d], { -1.0, 1.0, 0.0, 0.0 }, fCoord;" + "TEX s2, t2, texture[%d], %s;" + + "MAD t3, program.env[%d], { 1.0, -1.0, 0.0, 0.0 }, fCoord;" + "TEX s3, t3, texture[%d], %s;" + + "MUL_SAT mask, output.a, program.env[%d];" + + "MUL sum, s0, 0.25;" + "MAD sum, s1, 0.25, sum;" + "MAD sum, s2, 0.25, sum;" + "MAD sum, s3, 0.25, sum;", + + param + 2, unit, targetString, + param + 2, unit, targetString, + param + 2, unit, targetString, + param + 2, unit, targetString, + param + 1); + + } break; + case BlurOptions::FilterGaussian: { + + /* try to use only half of the available temporaries to keep + other plugins working */ + if ((maxTemp / 2) - 4 > + (numTexop + (numTexop - numITC)) * 2) + { + numIndirect = 1; + numIndirectOp = numTexop; + } + else + { + i = MAX (((maxTemp / 2) - 4) / 4, 1); + numIndirect = ceil ((float)numTexop / (float)i); + numIndirectOp = ceil ((float)numTexop / (float)numIndirect); + } + + /* we need to define all coordinate temporaries if we have + multiple indirection steps */ + j = (numIndirect > 1) ? 0 : numITC; + + for (i = 0; i < numIndirectOp * 2; i++) + { + snprintf (str, 1024, "pix_%d", i); + data.addTempHeaderOp (str); + } + + for (i = j * 2; i < numIndirectOp * 2; i++) + { + snprintf (str, 1024, "coord_%d", i); + data.addTempHeaderOp (str); + } + + data.addFetchOp ("output", NULL, target); + data.addColorOp ("output", "output"); + + data.addDataOp ( + "MUL fCoord, fragment.position, program.env[%d];", + param); + + + data.addDataOp ("TEX sum, fCoord, texture[%d], %s;", + unit + 1, targetString); + + + data.addDataOp ("MUL_SAT mask, output.a, program.env[%d];" + "MUL sum, sum, %f;", + param + 1, amp[numTexop]); + + for (j = 0; j < numIndirect; j++) + { + base = j * numIndirectOp; + end = MIN ((j + 1) * numIndirectOp, numTexop) - base; + + ITCbase = MAX (numITC - base, 0); + + for (i = ITCbase; i < end; i++) + { + data.addDataOp ( + "ADD coord_%d, fCoord, {0.0, %g, 0.0, 0.0};" + "SUB coord_%d, fCoord, {0.0, %g, 0.0, 0.0};", + i * 2, pos[base + i] * ty, + (i * 2) + 1, pos[base + i] * ty); + } + + for (i = 0; i < ITCbase; i++) + { + data.addDataOp ( + "TXP pix_%d, fragment.texcoord[%d], texture[%d], %s;" + "TXP pix_%d, fragment.texcoord[%d], texture[%d], %s;", + i * 2, startTC + ((i + base) * 2), + unit + 1, targetString, + (i * 2) + 1, startTC + 1 + ((i + base) * 2), + unit + 1, targetString); + } + + for (i = ITCbase; i < end; i++) + { + data.addDataOp ( + "TEX pix_%d, coord_%d, texture[%d], %s;" + "TEX pix_%d, coord_%d, texture[%d], %s;", + i * 2, i * 2, + unit + 1, targetString, + (i * 2) + 1, (i * 2) + 1, + unit + 1, targetString); + } + + for (i = 0; i < end * 2; i++) + { + data.addDataOp ( + "MAD sum, pix_%d, %f, sum;", + i, amp[base + (i / 2)]); + } + } + + } break; + case BlurOptions::FilterMipmap: + data.addFetchOp ("output", NULL, target); + data.addColorOp ("output", "output"); + + data.addDataOp ( + "MUL fCoord, fragment.position, program.env[%d].xyzz;" + "MOV fCoord.w, program.env[%d].w;" + "TXB sum, fCoord, texture[%d], %s;" + "MUL_SAT mask, output.a, program.env[%d];", + param, param, unit, targetString, + param + 1); + + break; + } + + if (saturation < 100) + { + data.addDataOp ( + "MUL sat, sum, { 1.0, 1.0, 1.0, 0.0 };" + "DP3 sat, sat, { %f, %f, %f, %f };" + "LRP sum.xyz, %f, sum, sat;", + RED_SATURATION_WEIGHT, GREEN_SATURATION_WEIGHT, + BLUE_SATURATION_WEIGHT, 0.0f, saturation / 100.0f); + } + + data.addDataOp ( + "MAD dst, mask, -output.a, mask;" + "MAD output.rgb, sum, dst.a, output;" + "ADD output.a, output.a, dst.a;"); + + if (!data.status ()) + { + return 0; + } + + + + function.id = data.createFragmentFunction ("blur"); + function.target = target; + function.param = param; + function.unit = unit; + function.numITC = numITC; + function.startTC = startTC; + + dstBlurFunctions.push_back (function); + + return function.id; + } + + return 0; +} + +bool +BlurScreen::projectVertices (CompOutput *output, + const GLMatrix &transform, + const float *object, + float *scr, + int n) +{ + GLdouble dProjection[16]; + GLdouble dModel[16]; + GLint viewport[4]; + double x, y, z; + int i; + + viewport[0] = output->x1 (); + viewport[1] = screen->height () - output->y2 (); + viewport[2] = output->width (); + viewport[3] = output->height (); + + for (i = 0; i < 16; i++) + { + dModel[i] = transform.getMatrix ()[i]; + dProjection[i] = gScreen->projectionMatrix ()[i]; + } + + while (n--) + { + if (!gluProject (object[0], object[1], object[2], + dModel, dProjection, viewport, + &x, &y, &z)) + return false; + + scr[0] = x; + scr[1] = y; + + object += 3; + scr += 2; + } + + return true; +} + +bool +BlurScreen::loadFragmentProgram (GLuint *program, + const char *string) +{ + GLint errorPos; + + /* clear errors */ + glGetError (); + + if (!*program) + (*GL::genPrograms) (1, program); + + (*GL::bindProgram) (GL_FRAGMENT_PROGRAM_ARB, *program); + (*GL::programString) (GL_FRAGMENT_PROGRAM_ARB, + GL_PROGRAM_FORMAT_ASCII_ARB, + strlen (string), string); + + glGetIntegerv (GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); + if (glGetError () != GL_NO_ERROR || errorPos != -1) + { + compLogMessage ("blur", CompLogLevelError, + "Failed to load blur program %s", string); + + (*GL::deletePrograms) (1, program); + *program = 0; + + return false; + } + + return true; +} + +bool +BlurScreen::loadFilterProgram (int numITC) +{ + char buffer[4096]; + char *targetString; + char *str = buffer; + int i, j; + int numIndirect; + int numIndirectOp; + int base, end, ITCbase; + + if (target == GL_TEXTURE_2D) + targetString = (char *) "2D"; + else + targetString = (char *) "RECT"; + + str += sprintf (str, + "!!ARBfp1.0" + "ATTRIB texcoord = fragment.texcoord[0];" + "TEMP sum;"); + + if (maxTemp - 1 > (numTexop + (numTexop - numITC)) * 2) + { + numIndirect = 1; + numIndirectOp = numTexop; + } + else + { + i = (maxTemp - 1) / 4; + numIndirect = ceil ((float)numTexop / (float)i); + numIndirectOp = ceil ((float)numTexop / (float)numIndirect); + } + + /* we need to define all coordinate temporaries if we have + multiple indirection steps */ + j = (numIndirect > 1) ? 0 : numITC; + + for (i = 0; i < numIndirectOp; i++) + str += sprintf (str,"TEMP pix_%d, pix_%d;", i * 2, (i * 2) + 1); + + for (i = j; i < numIndirectOp; i++) + str += sprintf (str,"TEMP coord_%d, coord_%d;", i * 2, (i * 2) + 1); + + str += sprintf (str, + "TEX sum, texcoord, texture[0], %s;", + targetString); + + str += sprintf (str, + "MUL sum, sum, %f;", + amp[numTexop]); + + for (j = 0; j < numIndirect; j++) + { + base = j * numIndirectOp; + end = MIN ((j + 1) * numIndirectOp, numTexop) - base; + + ITCbase = MAX (numITC - base, 0); + + for (i = ITCbase; i < end; i++) + str += sprintf (str, + "ADD coord_%d, texcoord, {%g, 0.0, 0.0, 0.0};" + "SUB coord_%d, texcoord, {%g, 0.0, 0.0, 0.0};", + i * 2, pos[base + i] * tx, + (i * 2) + 1, pos[base + i] * tx); + + for (i = 0; i < ITCbase; i++) + str += sprintf (str, + "TEX pix_%d, fragment.texcoord[%d], texture[0], %s;" + "TEX pix_%d, fragment.texcoord[%d], texture[0], %s;", + i * 2, ((i + base) * 2) + 1, targetString, + (i * 2) + 1, ((i + base) * 2) + 2, targetString); + + for (i = ITCbase; i < end; i++) + str += sprintf (str, + "TEX pix_%d, coord_%d, texture[0], %s;" + "TEX pix_%d, coord_%d, texture[0], %s;", + i * 2, i * 2, targetString, + (i * 2) + 1, (i * 2) + 1, targetString); + + for (i = 0; i < end * 2; i++) + str += sprintf (str, + "MAD sum, pix_%d, %f, sum;", + i, amp[base + (i / 2)]); + } + + str += sprintf (str, + "MOV result.color, sum;" + "END"); + + return loadFragmentProgram (&program, buffer); +} + +bool +BlurScreen::fboPrologue () +{ + if (!fbo) + return false; + + (*GL::bindFramebuffer) (GL_FRAMEBUFFER_EXT, fbo); + + /* bind texture and check status the first time */ + if (!fboStatus) + { + (*GL::framebufferTexture2D) (GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + target, texture[1], + 0); + + int currStatus = (*GL::checkFramebufferStatus) (GL_FRAMEBUFFER_EXT); + if (currStatus != GL_FRAMEBUFFER_COMPLETE_EXT) + { + compLogMessage ("blur", CompLogLevelError, + "Framebuffer incomplete"); + + (*GL::bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0); + (*GL::deleteFramebuffers) (1, &fbo); + + fbo = 0; + + return false; + } + else + fboStatus = true; + } + + glPushAttrib (GL_VIEWPORT_BIT | GL_ENABLE_BIT); + + glDrawBuffer (GL_COLOR_ATTACHMENT0_EXT); + glReadBuffer (GL_COLOR_ATTACHMENT0_EXT); + + glDisable (GL_CLIP_PLANE0); + glDisable (GL_CLIP_PLANE1); + glDisable (GL_CLIP_PLANE2); + glDisable (GL_CLIP_PLANE3); + + glViewport (0, 0, width, height); + glMatrixMode (GL_PROJECTION); + glPushMatrix (); + glLoadIdentity (); + glOrtho (0.0, width, 0.0, height, -1.0, 1.0); + glMatrixMode (GL_MODELVIEW); + glPushMatrix (); + glLoadIdentity (); + + return true; +} + +void +BlurScreen::fboEpilogue () +{ + (*GL::bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + glDepthRange (0, 1); + glViewport (-1, -1, 2, 2); + glRasterPos2f (0, 0); + + gScreen->resetRasterPos (); + + glMatrixMode (GL_PROJECTION); + glPopMatrix (); + glMatrixMode (GL_MODELVIEW); + glPopMatrix (); + + glDrawBuffer (GL_BACK); + glReadBuffer (GL_BACK); + + glPopAttrib (); +} + +bool +BlurScreen::fboUpdate (BoxPtr pBox, + int nBox) +{ + int i, y, iTC = 0; + bool wasCulled = glIsEnabled (GL_CULL_FACE); + + if (GL::maxTextureUnits && optionGetIndependentTex ()) + iTC = MIN ((GL::maxTextureUnits - 1) / 2, numTexop); + + if (!program) + if (!loadFilterProgram (iTC)) + return false; + + if (!fboPrologue ()) + return false; + + glDisable (GL_CULL_FACE); + + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + + glBindTexture (target, texture[0]); + + glEnable (GL_FRAGMENT_PROGRAM_ARB); + (*GL::bindProgram) (GL_FRAGMENT_PROGRAM_ARB, program); + + glBegin (GL_QUADS); + + while (nBox--) + { + y = screen->height () - pBox->y2; + + for (i = 0; i < iTC; i++) + { + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2), + tx * (pBox->x1 + pos[i]), + ty * y); + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1, + tx * (pBox->x1 - pos[i]), + ty * y); + } + + glTexCoord2f (tx * pBox->x1, ty * y); + glVertex2i (pBox->x1, y); + + for (i = 0; i < iTC; i++) + { + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2), + tx * (pBox->x2 + pos[i]), + ty * y); + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1, + tx * (pBox->x2 - pos[i]), + ty * y); + } + + glTexCoord2f (tx * pBox->x2, ty * y); + glVertex2i (pBox->x2, y); + + y = screen->height () - pBox->y1; + + for (i = 0; i < iTC; i++) + { + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2), + tx * (pBox->x2 + pos[i]), + ty * y); + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1, + tx * (pBox->x2 - pos[i]), + ty * y); + } + + glTexCoord2f (tx * pBox->x2, ty * y); + glVertex2i (pBox->x2, y); + + for (i = 0; i < iTC; i++) + { + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2), + tx * (pBox->x1 + pos[i]), + ty * y); + (*GL::multiTexCoord2f) (GL_TEXTURE1_ARB + (i * 2) + 1, + tx * (pBox->x1 - pos[i]), + ty * y); + } + + glTexCoord2f (tx * pBox->x1, ty * y); + glVertex2i (pBox->x1, y); + + pBox++; + } + + glEnd (); + + glDisable (GL_FRAGMENT_PROGRAM_ARB); + + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + + if (wasCulled) + glEnable (GL_CULL_FACE); + + fboEpilogue (); + + return true; +} + +#define MAX_VERTEX_PROJECT_COUNT 20 + +void +BlurWindow::projectRegion (CompOutput *output, + const GLMatrix &transform) +{ + float scrv[MAX_VERTEX_PROJECT_COUNT * 2]; + float vertices[MAX_VERTEX_PROJECT_COUNT * 3]; + int nVertices, nQuadCombine; + int i, j, stride; + float *v, *vert; + float minX, maxX, minY, maxY, minZ, maxZ; + float *scr; + + GLTexture::MatrixList ml; + GLWindow::Geometry *gm; + + gWindow->geometry ().reset (); + gWindow->glAddGeometry (ml, bScreen->tmpRegion2, infiniteRegion); + + if (!gWindow->geometry ().vCount) + return; + + gm = &gWindow->geometry (); + + nVertices = (gm->indexCount) ? gm->indexCount: gm->vCount; + nQuadCombine = 1; + + stride = gm->vertexStride; + vert = gm->vertices + (stride - 3); + + /* we need to find the best value here */ + if (nVertices <= MAX_VERTEX_PROJECT_COUNT) + { + for (i = 0; i < nVertices; i++) + { + if (gm->indexCount) + { + v = vert + (stride * gm->indices[i]); + } + else + { + v = vert + (stride * i); + } + + vertices[i * 3] = v[0]; + vertices[(i * 3) + 1] = v[1]; + vertices[(i * 3) + 2] = v[2]; + } + } + else + { + minX = screen->width (); + maxX = 0; + minY = screen->height (); + maxY = 0; + minZ = 1000000; + maxZ = -1000000; + + for (i = 0; i < gm->vCount; i++) + { + v = vert + (stride * i); + + if (v[0] < minX) + minX = v[0]; + + if (v[0] > maxX) + maxX = v[0]; + + if (v[1] < minY) + minY = v[1]; + + if (v[1] > maxY) + maxY = v[1]; + + if (v[2] < minZ) + minZ = v[2]; + + if (v[2] > maxZ) + maxZ = v[2]; + } + + vertices[0] = vertices[9] = minX; + vertices[1] = vertices[4] = minY; + vertices[3] = vertices[6] = maxX; + vertices[7] = vertices[10] = maxY; + vertices[2] = vertices[5] = maxZ; + vertices[8] = vertices[11] = maxZ; + + nVertices = 4; + + if (maxZ != minZ) + { + vertices[12] = vertices[21] = minX; + vertices[13] = vertices[16] = minY; + vertices[15] = vertices[18] = maxX; + vertices[19] = vertices[22] = maxY; + vertices[14] = vertices[17] = minZ; + vertices[20] = vertices[23] = minZ; + nQuadCombine = 2; + } + } + + if (!bScreen->projectVertices (output, transform, vertices, scrv, + nVertices * nQuadCombine)) + return; + + for (i = 0; i < nVertices / 4; i++) + { + scr = scrv + (i * 4 * 2); + + minX = screen->width (); + maxX = 0; + minY = screen->height (); + maxY = 0; + + for (j = 0; j < 8 * nQuadCombine; j += 2) + { + if (scr[j] < minX) + minX = scr[j]; + + if (scr[j] > maxX) + maxX = scr[j]; + + if (scr[j + 1] < minY) + minY = scr[j + 1]; + + if (scr[j + 1] > maxY) + maxY = scr[j + 1]; + } + + int x1, y1, x2, y2; + + x1 = minX - bScreen->filterRadius; + y1 = screen->height () - maxY - bScreen->filterRadius; + x2 = maxX + bScreen->filterRadius + 0.5f; + y2 = screen->height () - minY + bScreen->filterRadius + 0.5f; + + + bScreen->tmpRegion3 += CompRect (x1, y1, x2 - x1, y2 - y1); + + } +} + +bool +BlurWindow::updateDstTexture (const GLMatrix &transform, + CompRect *pExtents, + int clientThreshold) +{ + int y; + int filter; + + filter = bScreen->optionGetFilter (); + + bScreen->tmpRegion3 = CompRegion (); + + if (filter == BlurOptions::FilterGaussian) + { + + if (state[BLUR_STATE_DECOR].threshold) + { + int xx, yy, ww, hh; + // top + xx = window->x () - window->output ().left; + yy = window->y () - window->output ().top; + ww = window->width () + window->output ().left + + window->output ().right; + hh = window->output ().top; + + bScreen->tmpRegion2 = bScreen->tmpRegion.intersected ( + CompRect (xx, yy, ww, hh)); + + if (!bScreen->tmpRegion2.isEmpty ()) + projectRegion (bScreen->output, transform); + + // bottom + xx = window->x () - window->output ().left; + yy = window->y () + window->height (); + ww = window->width () + window->output ().left + + window->output ().right; + hh = window->output ().bottom; + + bScreen->tmpRegion2 = bScreen->tmpRegion.intersected ( + CompRect (xx, yy, ww, hh)); + + if (!bScreen->tmpRegion2.isEmpty ()) + projectRegion (bScreen->output, transform); + + // left + xx = window->x () - window->output ().left; + yy = window->y (); + ww = window->output ().left; + hh = window->height (); + + bScreen->tmpRegion2 = bScreen->tmpRegion.intersected ( + CompRect (xx, yy, ww, hh)); + + if (!bScreen->tmpRegion2.isEmpty ()) + projectRegion (bScreen->output, transform); + + // right + xx = window->x () + window->width (); + yy = window->y (); + ww = window->output ().right; + hh = window->height (); + + bScreen->tmpRegion2 = bScreen->tmpRegion.intersected ( + CompRect (xx, yy, ww, hh)); + + if (!bScreen->tmpRegion2.isEmpty ()) + projectRegion (bScreen->output, transform); + } + + if (clientThreshold) + { + // center + bScreen->tmpRegion2 = bScreen->tmpRegion.intersected ( + CompRect (window->x (), + window->y (), + window->width (), + window->height ())); + + if (!bScreen->tmpRegion2.isEmpty ()) + projectRegion (bScreen->output, transform); + } + } + else + { + // center + bScreen->tmpRegion2 = bScreen->tmpRegion; + + if (!bScreen->tmpRegion2.isEmpty ()) + projectRegion (bScreen->output, transform); + } + + bScreen->tmpRegion = bScreen->region.intersected (bScreen->tmpRegion3); + + if (bScreen->tmpRegion.isEmpty ()) + return false; + + *pExtents = bScreen->tmpRegion.boundingRect (); + + if (!bScreen->texture[0] || bScreen->width != screen->width () || + bScreen->height != screen->height ()) + { + int i, textures = 1; + + bScreen->width = screen->width (); + bScreen->height = screen->height (); + + if (GL::textureNonPowerOfTwo || + (POWER_OF_TWO (bScreen->width) && POWER_OF_TWO (bScreen->height))) + { + bScreen->target = GL_TEXTURE_2D; + bScreen->tx = 1.0f / bScreen->width; + bScreen->ty = 1.0f / bScreen->height; + } + else + { + bScreen->target = GL_TEXTURE_RECTANGLE_NV; + bScreen->tx = 1; + bScreen->ty = 1; + } + + if (filter == BlurOptions::FilterGaussian) + { + if (GL::fbo && !bScreen->fbo) + (*GL::genFramebuffers) (1, &bScreen->fbo); + + if (!bScreen->fbo) + compLogMessage ("blur", CompLogLevelError, + "Failed to create framebuffer object"); + + textures = 2; + } + + bScreen->fboStatus = false; + + for (i = 0; i < textures; i++) + { + if (!bScreen->texture[i]) + glGenTextures (1, &bScreen->texture[i]); + + glBindTexture (bScreen->target, bScreen->texture[i]); + + glTexImage2D (bScreen->target, 0, GL_RGB, + bScreen->width, + bScreen->height, + 0, GL_BGRA, + +#if IMAGE_BYTE_ORDER == MSBFirst + GL_UNSIGNED_INT_8_8_8_8_REV, +#else + GL_UNSIGNED_BYTE, +#endif + + NULL); + + glTexParameteri (bScreen->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (bScreen->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (filter == BlurOptions::FilterMipmap) + { + if (!GL::fbo) + { + compLogMessage ("blur", CompLogLevelWarn, + "GL_EXT_framebuffer_object extension " + "is required for mipmap filter"); + } + else if (bScreen->target != GL_TEXTURE_2D) + { + compLogMessage ("blur", CompLogLevelWarn, + "GL_ARB_texture_non_power_of_two " + "extension is required for mipmap filter"); + } + else + { + glTexParameteri (bScreen->target, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri (bScreen->target, GL_TEXTURE_MAG_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + } + } + + glTexParameteri (bScreen->target, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + glTexParameteri (bScreen->target, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + + glCopyTexSubImage2D (bScreen->target, 0, 0, 0, 0, 0, + bScreen->width, bScreen->height); + } + } + else + { + glBindTexture (bScreen->target, bScreen->texture[0]); + + CompRect br = bScreen->tmpRegion.boundingRect (); + + y = screen->height () - br.y2 (); + + glCopyTexSubImage2D (bScreen->target, 0, + br.x1 (), y, + br.x1 (), y, + br.width (), + br.height ()); + } + + switch (filter) { + case BlurOptions::FilterGaussian: + return bScreen->fboUpdate (bScreen->tmpRegion.handle ()->rects, + bScreen->tmpRegion.numRects ()); + case BlurOptions::FilterMipmap: + if (GL::generateMipmap) + (*GL::generateMipmap) (bScreen->target); + break; + case BlurOptions::Filter4xbilinear: + break; + } + + glBindTexture (bScreen->target, 0); + + return true; +} + +bool +BlurWindow::glDraw (const GLMatrix &transform, + GLFragment::Attrib &attrib, + const CompRegion ®ion, + unsigned int mask) +{ + bool status; + + if (bScreen->alphaBlur && !region.isEmpty ()) + { + int clientThreshold; + + /* only care about client window blurring when it's translucent */ + if (mask & PAINT_WINDOW_TRANSLUCENT_MASK) + clientThreshold = state[BLUR_STATE_CLIENT].threshold; + else + clientThreshold = 0; + + if (state[BLUR_STATE_DECOR].threshold || clientThreshold) + { + bool clipped = false; + CompRect box (0, 0, 0, 0); + CompRegion reg; + + bScreen->mvp = GLMatrix (bScreen->gScreen->projectionMatrix ()); + bScreen->mvp *= transform; + + if (mask & PAINT_WINDOW_TRANSFORMED_MASK) + reg = infiniteRegion; + else + reg = region; + + bScreen->tmpRegion = this->region.intersected (reg); + if (!bScreen->blurOcclusion && + !(mask & PAINT_WINDOW_TRANSFORMED_MASK)) + bScreen->tmpRegion -= clip; + + if (updateDstTexture (transform, &box, clientThreshold)) + { + if (clientThreshold) + { + if (state[BLUR_STATE_CLIENT].clipped) + { + if (bScreen->stencilBits) + { + state[BLUR_STATE_CLIENT].active = true; + clipped = true; + } + } + else + { + state[BLUR_STATE_CLIENT].active = true; + } + } + + if (state[BLUR_STATE_DECOR].threshold) + { + if (state[BLUR_STATE_DECOR].clipped) + { + if (bScreen->stencilBits) + { + state[BLUR_STATE_DECOR].active = true; + clipped = true; + } + } + else + { + state[BLUR_STATE_DECOR].active = true; + } + } + + if (!bScreen->blurOcclusion && !clip.isEmpty ()) + clipped = true; + } + + if (!bScreen->blurOcclusion) + bScreen->tmpRegion = this->region - clip; + else + bScreen->tmpRegion = this->region; + + if (!clientThreshold) + { + bScreen->tmpRegion -= CompRect (window->x (), + window->x () + window->width (), + window->y (), + window->y () + window->height ()); + } + + if (clipped) + { + GLTexture::MatrixList ml; + + gWindow->geometry ().reset (); + + gWindow->glAddGeometry (ml, bScreen->tmpRegion, reg); + if (gWindow->geometry ().vCount) + { + CompRect clearBox = bScreen->stencilBox; + + bScreen->stencilBox = box; + + glEnable (GL_STENCIL_TEST); + glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + if (clearBox.x2 () > clearBox.x1 () && + clearBox.y2 () > clearBox.y1 ()) + { + glPushAttrib (GL_SCISSOR_BIT); + glEnable (GL_SCISSOR_TEST); + glScissor (clearBox.x1 (), + screen->height () - clearBox.y2 (), + clearBox.width (), + clearBox.height ()); + glClear (GL_STENCIL_BUFFER_BIT); + glPopAttrib (); + } + + glStencilFunc (GL_ALWAYS, 0x1, ~0); + glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE); + + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + gWindow->glDrawGeometry (); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + + glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDisable (GL_STENCIL_TEST); + } + } + } + } + + status = gWindow->glDraw (transform, attrib, region, mask); + + state[BLUR_STATE_CLIENT].active = false; + state[BLUR_STATE_DECOR].active = false; + + return status; +} + +void +BlurWindow::glDrawTexture (GLTexture *texture, + GLFragment::Attrib &attrib, + unsigned int mask) +{ + int state = BLUR_STATE_DECOR; + + foreach (GLTexture *tex, gWindow->textures ()) + if (texture == tex) + state = BLUR_STATE_CLIENT; + + if (blur || this->state[state].active) + { + GLFragment::Attrib fa (attrib); + int param, function; + int unit = 0; + GLfloat dx, dy; + int iTC = 0; + + if (blur) + { + param = fa.allocParameters (1); + + function = bScreen->getSrcBlurFragmentFunction (texture, param); + if (function) + { + fa.addFunction (function); + + dx = ((texture->matrix ().xx / 2.1f) * blur) / 65535.0f; + dy = ((texture->matrix ().yy / 2.1f) * blur) / 65535.0f; + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param, dx, dy, dx, -dy); + + /* bi-linear filtering is required */ + mask |= PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK; + } + } + + if (this->state[state].active) + { + GLFragment::Attrib dstFa (fa); + float threshold = (float) this->state[state].threshold; + + switch (bScreen->optionGetFilter ()) { + case BlurOptions::Filter4xbilinear: + dx = bScreen->tx / 2.1f; + dy = bScreen->ty / 2.1f; + + param = dstFa.allocParameters (3); + unit = dstFa.allocTextureUnits (1); + + function = bScreen->getDstBlurFragmentFunction ( + texture, param, unit, 0, 0); + if (function) + { + dstFa.addFunction (function); + + (*GL::activeTexture) (GL_TEXTURE0_ARB + unit); + glBindTexture (bScreen->target, bScreen->texture[0]); + (*GL::activeTexture) (GL_TEXTURE0_ARB); + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param, + bScreen->tx, bScreen->ty, + 0.0f, 0.0f); + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param + 1, + threshold, threshold, + threshold, threshold); + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param + 2, + dx, dy, 0.0f, 0.0f); + } + break; + case BlurOptions::FilterGaussian: + if (bScreen->optionGetIndependentTex ()) + { + /* leave one free texture unit for fragment position */ + iTC = MAX (0, GL::maxTextureUnits - + (gWindow->geometry ().texUnits + 1)); + if (iTC) + iTC = MIN (iTC / 2, bScreen->numTexop); + } + + param = dstFa.allocParameters (2); + unit = dstFa.allocTextureUnits (2); + + function = bScreen->getDstBlurFragmentFunction ( + texture, param, unit, iTC, + gWindow->geometry ().texUnits); + + if (function) + { + int i; + + dstFa.addFunction (function); + + (*GL::activeTexture) (GL_TEXTURE0_ARB + unit); + glBindTexture (bScreen->target, bScreen->texture[0]); + (*GL::activeTexture) (GL_TEXTURE0_ARB + unit + 1); + glBindTexture (bScreen->target, bScreen->texture[1]); + (*GL::activeTexture) (GL_TEXTURE0_ARB); + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param, bScreen->tx, + bScreen->ty, 0.0f, 0.0f); + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param + 1, + threshold, threshold, + threshold, threshold); + + if (iTC) + { + GLMatrix tm, rm; + float s_gen[4], t_gen[4], q_gen[4]; + + for (i = 0; i < 16; i++) + tm[i] = 0; + tm[0] = (bScreen->output->width () / 2.0) * + bScreen->tx; + tm[5] = (bScreen->output->height () / 2.0) * + bScreen->ty; + tm[10] = 1; + + tm[12] = (bScreen->output->width () / 2.0 + + bScreen->output->x1 ()) * bScreen->tx; + tm[13] = (bScreen->output->height () / 2.0 + + screen->height () - + bScreen->output->y2 ()) * bScreen->ty; + tm[14] = 1; + tm[15] = 1; + + tm *= bScreen->mvp; + + for (i = 0; i < iTC; i++) + { + (*GL::activeTexture) (GL_TEXTURE0_ARB + + gWindow->geometry ().texUnits + (i * 2)); + + rm.reset (); + rm[13] = bScreen->ty * bScreen->pos[i]; + rm *= tm; + + s_gen[0] = rm[0]; + s_gen[1] = rm[4]; + s_gen[2] = rm[8]; + s_gen[3] = rm[12]; + t_gen[0] = rm[1]; + t_gen[1] = rm[5]; + t_gen[2] = rm[9]; + t_gen[3] = rm[13]; + q_gen[0] = rm[3]; + q_gen[1] = rm[7]; + q_gen[2] = rm[11]; + q_gen[3] = rm[15]; + + glTexGenfv (GL_T, GL_OBJECT_PLANE, t_gen); + glTexGenfv (GL_S, GL_OBJECT_PLANE, s_gen); + glTexGenfv (GL_Q, GL_OBJECT_PLANE, q_gen); + + glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, + GL_OBJECT_LINEAR); + glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, + GL_OBJECT_LINEAR); + glTexGeni (GL_Q, GL_TEXTURE_GEN_MODE, + GL_OBJECT_LINEAR); + + glEnable (GL_TEXTURE_GEN_S); + glEnable (GL_TEXTURE_GEN_T); + glEnable (GL_TEXTURE_GEN_Q); + + (*GL::activeTexture) (GL_TEXTURE0_ARB + + gWindow->geometry ().texUnits + + 1 + (i * 2)); + + rm.reset (); + + rm[13] = -bScreen->ty * bScreen->pos[i]; + rm *= tm; + + s_gen[0] = rm[0]; + s_gen[1] = rm[4]; + s_gen[2] = rm[8]; + s_gen[3] = rm[12]; + t_gen[0] = rm[1]; + t_gen[1] = rm[5]; + t_gen[2] = rm[9]; + t_gen[3] = rm[13]; + q_gen[0] = rm[3]; + q_gen[1] = rm[7]; + q_gen[2] = rm[11]; + q_gen[3] = rm[15]; + + glTexGenfv (GL_T, GL_OBJECT_PLANE, t_gen); + glTexGenfv (GL_S, GL_OBJECT_PLANE, s_gen); + glTexGenfv (GL_Q, GL_OBJECT_PLANE, q_gen); + + glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, + GL_OBJECT_LINEAR); + glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, + GL_OBJECT_LINEAR); + glTexGeni (GL_Q, GL_TEXTURE_GEN_MODE, + GL_OBJECT_LINEAR); + + glEnable (GL_TEXTURE_GEN_S); + glEnable (GL_TEXTURE_GEN_T); + glEnable (GL_TEXTURE_GEN_Q); + } + + (*GL::activeTexture) (GL_TEXTURE0_ARB); + } + + } + break; + case BlurOptions::FilterMipmap: + param = dstFa.allocParameters (2); + unit = dstFa.allocTextureUnits (1); + + function = + bScreen->getDstBlurFragmentFunction (texture, param, + unit, 0, 0); + if (function) + { + float lod = + bScreen->optionGetMipmapLod (); + + dstFa.addFunction (function); + + (*GL::activeTexture) (GL_TEXTURE0_ARB + unit); + glBindTexture (bScreen->target, bScreen->texture[0]); + (*GL::activeTexture) (GL_TEXTURE0_ARB); + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param, + bScreen->tx, bScreen->ty, + 0.0f, lod); + + (*GL::programEnvParameter4f) (GL_FRAGMENT_PROGRAM_ARB, + param + 1, + threshold, threshold, + threshold, threshold); + } + break; + } + + if (this->state[state].clipped || + (!bScreen->blurOcclusion && !clip.isEmpty ())) + { + glEnable (GL_STENCIL_TEST); + + glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc (GL_EQUAL, 0x1, ~0); + + /* draw region with destination blur */ + gWindow->glDrawTexture (texture, dstFa, mask); + + glStencilFunc (GL_EQUAL, 0, ~0); + + /* draw region without destination blur */ + gWindow->glDrawTexture (texture, fa, mask); + + glDisable (GL_STENCIL_TEST); + } + else + { + /* draw with destination blur */ + gWindow->glDrawTexture (texture, dstFa, mask); + } + } + else + { + gWindow->glDrawTexture (texture, fa, mask); + } + + if (unit) + { + (*GL::activeTexture) (GL_TEXTURE0_ARB + unit); + glBindTexture (bScreen->target, 0); + (*GL::activeTexture) (GL_TEXTURE0_ARB + unit + 1); + glBindTexture (bScreen->target, 0); + (*GL::activeTexture) (GL_TEXTURE0_ARB); + } + + if (iTC) + { + int i; + for (i = gWindow->geometry ().texUnits; + i < gWindow->geometry ().texUnits + (2 * iTC); i++) + { + (*GL::activeTexture) (GL_TEXTURE0_ARB + i); + glDisable (GL_TEXTURE_GEN_S); + glDisable (GL_TEXTURE_GEN_T); + glDisable (GL_TEXTURE_GEN_Q); + } + (*GL::activeTexture) (GL_TEXTURE0_ARB); + } + } + else + { + gWindow->glDrawTexture (texture, attrib, mask); + } +} + +void +BlurScreen::handleEvent (XEvent *event) +{ + Window activeWindow = screen->activeWindow (); + + screen->handleEvent (event); + + if (screen->activeWindow () != activeWindow) + { + CompWindow *w; + + w = screen->findWindow (activeWindow); + if (w) + { + if (optionGetFocusBlur ()) + { + CompositeWindow::get (w)->addDamage (); + moreBlur = true; + } + } + + w = screen->findWindow (screen->activeWindow ()); + if (w) + { + if (optionGetFocusBlur ()) + { + CompositeWindow::get (w)->addDamage (); + moreBlur = true; + } + } + } + + if (event->type == PropertyNotify) + { + int i; + + for (i = 0; i < BLUR_STATE_NUM; i++) + { + if (event->xproperty.atom == blurAtom[i]) + { + CompWindow *w; + + w = screen->findWindow (event->xproperty.window); + if (w) + BlurWindow::get (w)->update (i); + } + } + } +} + +void +BlurWindow::resizeNotify (int dx, + int dy, + int dwidth, + int dheight) +{ + if (bScreen->alphaBlur) + { + if (state[BLUR_STATE_CLIENT].threshold || + state[BLUR_STATE_DECOR].threshold) + updateRegion (); + } + + window->resizeNotify (dx, dy, dwidth, dheight); + +} + +void +BlurWindow::moveNotify (int dx, + int dy, + bool immediate) +{ + if (!region.isEmpty ()) + region.translate (dx, dy); + + window->moveNotify (dx, dy, immediate); +} + +static bool +blurPulse (CompAction *action, + CompAction::State state, + CompOption::Vector &options) +{ + CompWindow *w; + int xid; + + xid = CompOption::getIntOptionNamed (options, "window", + screen->activeWindow ()); + + w = screen->findWindow (xid); + if (w && GL::fragmentProgram) + { + BLUR_SCREEN (screen); + BLUR_WINDOW (w); + + bw->pulse = true; + bs->moreBlur = true; + + bw->cWindow->addDamage (); + } + + return false; +} + +void +BlurScreen::matchExpHandlerChanged () +{ + screen->matchExpHandlerChanged (); + + /* match options are up to date after the call to matchExpHandlerChanged */ + foreach (CompWindow *w, screen->windows ()) + BlurWindow::get (w)->updateMatch (); + +} + +void +BlurScreen::matchPropertyChanged (CompWindow *w) +{ + BlurWindow::get (w)->updateMatch (); + + screen->matchPropertyChanged (w); +} + +bool +BlurScreen::setOption (const CompString &name, CompOption::Value &value) +{ + unsigned int index; + + bool rv = BlurOptions::setOption (name, value); + + if (!rv || !CompOption::findOption (getOptions (), name, &index)) + return false; + + switch (index) { + case BlurOptions::BlurSpeed: + blurTime = 1000.0f / optionGetBlurSpeed (); + break; + case BlurOptions::FocusBlurMatch: + case BlurOptions::AlphaBlurMatch: + foreach (CompWindow *w, screen->windows ()) + BlurWindow::get (w)->updateMatch (); + + moreBlur = true; + cScreen->damageScreen (); + break; + case BlurOptions::FocusBlur: + moreBlur = true; + cScreen->damageScreen (); + break; + case BlurOptions::AlphaBlur: + if (GL::fragmentProgram && optionGetAlphaBlur ()) + alphaBlur = true; + else + alphaBlur = false; + + cScreen->damageScreen (); + break; + case BlurOptions::Filter: + blurReset (); + cScreen->damageScreen (); + break; + case BlurOptions::GaussianRadius: + case BlurOptions::GaussianStrength: + case BlurOptions::IndependentTex: + if (optionGetFilter () == BlurOptions::FilterGaussian) + { + blurReset (); + cScreen->damageScreen (); + } + break; + case BlurOptions::MipmapLod: + if (optionGetFilter () == BlurOptions::FilterMipmap) + { + blurReset (); + cScreen->damageScreen (); + } + break; + case BlurOptions::Saturation: + blurReset (); + cScreen->damageScreen (); + break; + case BlurOptions::Occlusion: + blurOcclusion = optionGetOcclusion (); + blurReset (); + cScreen->damageScreen (); + break; + default: + break; + } + + return rv; +} + +BlurScreen::BlurScreen (CompScreen *screen) : + PluginClassHandler<BlurScreen,CompScreen> (screen), + gScreen (GLScreen::get (screen)), + cScreen (CompositeScreen::get (screen)), + moreBlur (false), + filterRadius (0), + srcBlurFunctions (0), + dstBlurFunctions (0), + output (NULL), + count (0), + program (0), + maxTemp (32), + fbo (0), + fboStatus (0) +{ + + blurAtom[BLUR_STATE_CLIENT] = + XInternAtom (screen->dpy (), "_COMPIZ_WM_WINDOW_BLUR", 0); + blurAtom[BLUR_STATE_DECOR] = + XInternAtom (screen->dpy (), DECOR_BLUR_ATOM_NAME, 0); + + blurTime = 1000.0f / optionGetBlurSpeed (); + blurOcclusion = optionGetOcclusion (); + + for (int i = 0; i < 2; i++) + texture[i] = 0; + + glGetIntegerv (GL_STENCIL_BITS, &stencilBits); + if (!stencilBits) + compLogMessage ("blur", CompLogLevelWarn, + "No stencil buffer. Region based blur disabled"); + + /* We need GL_ARB_fragment_program for blur */ + if (GL::fragmentProgram) + alphaBlur = optionGetAlphaBlur (); + else + alphaBlur = false; + + if (GL::fragmentProgram) + { + int tmp[4]; + GL::getProgramiv (GL_FRAGMENT_PROGRAM_ARB, + GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB, + tmp); + maxTemp = tmp[0]; + } + + updateFilterRadius (); + + optionSetPulseInitiate (blurPulse); + + ScreenInterface::setHandler (screen, true); + CompositeScreenInterface::setHandler (cScreen, true); + GLScreenInterface::setHandler (gScreen, true); +} + + +BlurScreen::~BlurScreen () +{ + foreach (BlurFunction &bf, srcBlurFunctions) + GLFragment::destroyFragmentFunction (bf.id); + foreach (BlurFunction &bf, dstBlurFunctions) + GLFragment::destroyFragmentFunction (bf.id); + + cScreen->damageScreen (); + + if (fbo) + (*GL::deleteFramebuffers) (1, &fbo); + + for (int i = 0; i < 2; i++) + if (texture[i]) + glDeleteTextures (1, &texture[i]); + +} + +BlurWindow::BlurWindow (CompWindow *w) : + PluginClassHandler<BlurWindow,CompWindow> (w), + window (w), + cWindow (CompositeWindow::get (w)), + gWindow (GLWindow::get (w)), + bScreen (BlurScreen::get (screen)), + blur (0), + pulse (false), + focusBlur (false) +{ + for (int i = 0; i < BLUR_STATE_NUM; i++) + { + state[i].threshold = 0; + state[i].clipped = false; + state[i].active = false; + + propSet[i] = false; + } + + update (BLUR_STATE_CLIENT); + update (BLUR_STATE_DECOR); + + updateMatch (); + + + WindowInterface::setHandler (window, true); + GLWindowInterface::setHandler (gWindow, true); +} + +BlurWindow::~BlurWindow () +{ +} + +bool +BlurPluginVTable::init () +{ + if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) | + !CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) | + !CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI)) + return false; + + return true; +} + diff --git a/plugins/blur/src/blur.h b/plugins/blur/src/blur.h new file mode 100644 index 0000000..a03c5dd --- /dev/null +++ b/plugins/blur/src/blur.h @@ -0,0 +1,241 @@ +/* + * Copyright © 2007 Novell, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of + * Novell, Inc. not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior permission. + * Novell, Inc. makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: David Reveman <davidr@novell.com> + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <core/core.h> +#include <composite/composite.h> +#include <opengl/opengl.h> +#include <decoration.h> + +#include <X11/Xatom.h> +#include <GL/glu.h> + +#include "blur_options.h" + +#define BLUR_GAUSSIAN_RADIUS_MAX 15 + +struct BlurFunction { + + GLFragment::FunctionId id; + + int target; + int param; + int unit; + int startTC; + int numITC; +}; + +struct BlurBox { + decor_point_t p1; + decor_point_t p2; +}; + +#define BLUR_STATE_CLIENT 0 +#define BLUR_STATE_DECOR 1 +#define BLUR_STATE_NUM 2 + +struct BlurState { + int threshold; + std::vector<BlurBox> box; + bool active; + bool clipped; +}; + +class BlurScreen : + public ScreenInterface, + public CompositeScreenInterface, + public GLScreenInterface, + public PluginClassHandler<BlurScreen,CompScreen>, + public BlurOptions +{ + public: + BlurScreen (CompScreen *screen); + ~BlurScreen (); + + bool setOption (const CompString &name, CompOption::Value &value); + + void handleEvent (XEvent *); + + virtual void matchExpHandlerChanged (); + virtual void matchPropertyChanged (CompWindow *window); + + void preparePaint (int); + void donePaint (); + + bool glPaintOutput (const GLScreenPaintAttrib &, + const GLMatrix &, const CompRegion &, + CompOutput *, unsigned int); + void glPaintTransformedOutput (const GLScreenPaintAttrib &, + const GLMatrix &, + const CompRegion &, + CompOutput *, unsigned int); + + void updateFilterRadius (); + void blurReset (); + + GLFragment::FunctionId getSrcBlurFragmentFunction (GLTexture *, int); + GLFragment::FunctionId getDstBlurFragmentFunction (GLTexture *texture, + int param, + int unit, + int numITC, + int startTC); + + bool projectVertices (CompOutput *output, + const GLMatrix &transform, + const float *object, + float *scr, + int n); + + bool loadFragmentProgram (GLuint *program, + const char *string); + + bool loadFilterProgram (int numITC); + + bool fboPrologue (); + void fboEpilogue (); + bool fboUpdate (BoxPtr pBox, int nBox); + + + public: + GLScreen *gScreen; + CompositeScreen *cScreen; + + Atom blurAtom[BLUR_STATE_NUM]; + + bool alphaBlur; + + int blurTime; + bool moreBlur; + + bool blurOcclusion; + + int filterRadius; + + std::vector<BlurFunction> srcBlurFunctions; + std::vector<BlurFunction> dstBlurFunctions; + + CompRegion region; + CompRegion tmpRegion; + CompRegion tmpRegion2; + CompRegion tmpRegion3; + CompRegion occlusion; + + CompRect stencilBox; + GLint stencilBits; + + CompOutput *output; + int count; + + GLuint texture[2]; + + GLenum target; + float tx; + float ty; + int width; + int height; + + GLuint program; + int maxTemp; + GLuint fbo; + bool fboStatus; + + float amp[BLUR_GAUSSIAN_RADIUS_MAX]; + float pos[BLUR_GAUSSIAN_RADIUS_MAX]; + int numTexop; + + GLMatrix mvp; +}; + +class BlurWindow : + public WindowInterface, + public GLWindowInterface, + public PluginClassHandler<BlurWindow,CompWindow> +{ + + public: + + BlurWindow (CompWindow *window); + ~BlurWindow (); + + void resizeNotify (int dx, int dy, int dwidth, int dheight); + void moveNotify (int dx, int dy, bool immediate); + + bool glPaint (const GLWindowPaintAttrib &, const GLMatrix &, + const CompRegion &, unsigned int); + bool glDraw (const GLMatrix &, GLFragment::Attrib &, + const CompRegion &, unsigned int); + void glDrawTexture (GLTexture *texture, GLFragment::Attrib &, + unsigned int); + + void updateRegion (); + + void setBlur (int state, + int threshold, + std::vector<BlurBox> box); + + void updateAlphaMatch (); + void updateMatch (); + void update (int state); + + void projectRegion (CompOutput *output, + const GLMatrix &transform); + + bool updateDstTexture (const GLMatrix &transform, + CompRect *pExtents, + int clientThreshold); + + public: + CompWindow *window; + CompositeWindow *cWindow; + GLWindow *gWindow; + BlurScreen *bScreen; + + int blur; + bool pulse; + bool focusBlur; + + BlurState state[BLUR_STATE_NUM]; + bool propSet[BLUR_STATE_NUM]; + + CompRegion region; + CompRegion clip; +}; + +#define BLUR_SCREEN(s) \ + BlurScreen *bs = BlurScreen::get (s) + +#define BLUR_WINDOW(w) \ + BlurWindow *bw = BlurWindow::get (w) + +class BlurPluginVTable : + public CompPlugin::VTableForScreenAndWindow<BlurScreen,BlurWindow> +{ + public: + + bool init (); +}; |