Blame src/st/st-shadow.c

Packit d345d1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
Packit d345d1
/*
Packit d345d1
 * st-shadow.c: Boxed type holding for -st-shadow attributes
Packit d345d1
 *
Packit d345d1
 * Copyright 2009, 2010 Florian Müllner
Packit d345d1
 *
Packit d345d1
 * This program is free software; you can redistribute it and/or modify
Packit d345d1
 * it under the terms of the GNU Lesser General Public License as
Packit d345d1
 * published by the Free Software Foundation, either version 2.1 of
Packit d345d1
 * the License, or (at your option) any later version.
Packit d345d1
 *
Packit d345d1
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
Packit d345d1
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
Packit d345d1
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
Packit d345d1
 * more details.
Packit d345d1
 *
Packit d345d1
 * You should have received a copy of the GNU Lesser General Public License
Packit d345d1
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Packit d345d1
 */
Packit d345d1
Packit d345d1
#include "config.h"
Packit d345d1
Packit d345d1
#include "st-shadow.h"
Packit d345d1
#include "st-private.h"
Packit d345d1
Packit d345d1
G_DEFINE_BOXED_TYPE (StShadow, st_shadow, st_shadow_ref, st_shadow_unref)
Packit d345d1
G_DEFINE_BOXED_TYPE (StShadowHelper, st_shadow_helper, st_shadow_helper_copy, st_shadow_helper_free)
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * SECTION: st-shadow
Packit d345d1
 * @short_description: Boxed type for -st-shadow attributes
Packit d345d1
 *
Packit d345d1
 * #StShadow is a boxed type for storing attributes of the -st-shadow
Packit d345d1
 * property, modelled liberally after the CSS3 box-shadow property.
Packit d345d1
 * See http://www.css3.info/preview/box-shadow/
Packit d345d1
 *
Packit d345d1
 */
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_new:
Packit d345d1
 * @color: shadow's color
Packit d345d1
 * @xoffset: horizontal offset
Packit d345d1
 * @yoffset: vertical offset
Packit d345d1
 * @blur: blur radius
Packit d345d1
 * @spread: spread radius
Packit d345d1
 * @inset: whether the shadow should be inset
Packit d345d1
 *
Packit d345d1
 * Creates a new #StShadow
Packit d345d1
 *
Packit d345d1
 * Returns: the newly allocated shadow. Use st_shadow_free() when done
Packit d345d1
 */
Packit d345d1
StShadow *
Packit d345d1
st_shadow_new (ClutterColor *color,
Packit d345d1
               gdouble       xoffset,
Packit d345d1
               gdouble       yoffset,
Packit d345d1
               gdouble       blur,
Packit d345d1
               gdouble       spread,
Packit d345d1
               gboolean      inset)
Packit d345d1
{
Packit d345d1
  StShadow *shadow;
Packit d345d1
Packit d345d1
  shadow = g_slice_new (StShadow);
Packit d345d1
Packit d345d1
  shadow->color     = *color;
Packit d345d1
  shadow->xoffset   = xoffset;
Packit d345d1
  shadow->yoffset   = yoffset;
Packit d345d1
  shadow->blur      = blur;
Packit d345d1
  shadow->spread    = spread;
Packit d345d1
  shadow->inset     = inset;
Packit d345d1
  shadow->ref_count = 1;
Packit d345d1
Packit d345d1
  return shadow;
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_ref:
Packit d345d1
 * @shadow: a #StShadow
Packit d345d1
 *
Packit d345d1
 * Atomically increments the reference count of @shadow by one.
Packit d345d1
 *
Packit d345d1
 * Returns: the passed in #StShadow.
Packit d345d1
 */
Packit d345d1
StShadow *
Packit d345d1
st_shadow_ref (StShadow *shadow)
Packit d345d1
{
Packit d345d1
  g_return_val_if_fail (shadow != NULL, NULL);
Packit d345d1
  g_return_val_if_fail (shadow->ref_count > 0, shadow);
Packit d345d1
Packit d345d1
  g_atomic_int_inc (&shadow->ref_count);
Packit d345d1
  return shadow;
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_unref:
Packit d345d1
 * @shadow: a #StShadow
Packit d345d1
 *
Packit d345d1
 * Atomically decrements the reference count of @shadow by one.
Packit d345d1
 * If the reference count drops to 0, all memory allocated by the
Packit d345d1
 * #StShadow is released.
Packit d345d1
 */
Packit d345d1
void
Packit d345d1
st_shadow_unref (StShadow *shadow)
Packit d345d1
{
Packit d345d1
  g_return_if_fail (shadow != NULL);
Packit d345d1
  g_return_if_fail (shadow->ref_count > 0);
Packit d345d1
Packit d345d1
  if (g_atomic_int_dec_and_test (&shadow->ref_count))
Packit d345d1
    g_slice_free (StShadow, shadow);
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_equal:
Packit d345d1
 * @shadow: a #StShadow
Packit d345d1
 * @other: a different #StShadow
Packit d345d1
 *
Packit d345d1
 * Check if two shadow objects are identical. Note that two shadows may
Packit d345d1
 * compare non-identically if they differ only by floating point rounding
Packit d345d1
 * errors.
Packit d345d1
 *
Packit d345d1
 * Return value: %TRUE if the two shadows are identical
Packit d345d1
 */
Packit d345d1
gboolean
Packit d345d1
st_shadow_equal (StShadow *shadow,
Packit d345d1
                 StShadow *other)
Packit d345d1
{
Packit d345d1
  g_return_val_if_fail (shadow != NULL, FALSE);
Packit d345d1
  g_return_val_if_fail (other != NULL, FALSE);
Packit d345d1
Packit d345d1
  /* We use strict equality to compare double quantities; this means
Packit d345d1
   * that, for example, a shadow offset of 0.25in does not necessarily
Packit d345d1
   * compare equal to a shadow offset of 18pt in this test. Assume
Packit d345d1
   * that a few false negatives are mostly harmless.
Packit d345d1
   */
Packit d345d1
Packit d345d1
  return (clutter_color_equal (&shadow->color, &other->color) &&
Packit d345d1
          shadow->xoffset == other->xoffset &&
Packit d345d1
          shadow->yoffset == other->yoffset &&
Packit d345d1
          shadow->blur == other->blur &&
Packit d345d1
          shadow->spread == other->spread &&
Packit d345d1
          shadow->inset == other->inset);
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_get_box:
Packit d345d1
 * @shadow: a #StShadow
Packit d345d1
 * @actor_box: the box allocated to a #ClutterAlctor
Packit d345d1
 * @shadow_box: computed box occupied by @shadow
Packit d345d1
 *
Packit d345d1
 * Gets the box used to paint @shadow, which will be partly
Packit d345d1
 * outside of @actor_box
Packit d345d1
 */
Packit d345d1
void
Packit d345d1
st_shadow_get_box (StShadow              *shadow,
Packit d345d1
                   const ClutterActorBox *actor_box,
Packit d345d1
                   ClutterActorBox       *shadow_box)
Packit d345d1
{
Packit d345d1
  g_return_if_fail (shadow != NULL);
Packit d345d1
  g_return_if_fail (actor_box != NULL);
Packit d345d1
  g_return_if_fail (shadow_box != NULL);
Packit d345d1
Packit d345d1
  /* Inset shadows are drawn below the border, so returning
Packit d345d1
   * the original box is not actually correct; still, it's
Packit d345d1
   * good enough for the purpose of determing additional space
Packit d345d1
   * required outside the actor box.
Packit d345d1
   */
Packit d345d1
  if (shadow->inset)
Packit d345d1
    {
Packit d345d1
      *shadow_box = *actor_box;
Packit d345d1
      return;
Packit d345d1
    }
Packit d345d1
Packit d345d1
  shadow_box->x1 = actor_box->x1 + shadow->xoffset
Packit d345d1
                   - shadow->blur - shadow->spread;
Packit d345d1
  shadow_box->x2 = actor_box->x2 + shadow->xoffset
Packit d345d1
                   + shadow->blur + shadow->spread;
Packit d345d1
  shadow_box->y1 = actor_box->y1 + shadow->yoffset
Packit d345d1
                   - shadow->blur - shadow->spread;
Packit d345d1
  shadow_box->y2 = actor_box->y2 + shadow->yoffset
Packit d345d1
                   + shadow->blur + shadow->spread;
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * SECTION: st-shadow-helper
Packit d345d1
 *
Packit d345d1
 * An helper for implementing a drop shadow on a actor.
Packit d345d1
 * The actor is expected to recreate the helper whenever its contents
Packit d345d1
 * or size change. Then, it would call st_shadow_helper_paint() inside
Packit d345d1
 * its paint() virtual function.
Packit d345d1
 */
Packit d345d1
Packit d345d1
struct _StShadowHelper {
Packit d345d1
  StShadow     *shadow;
Packit d345d1
  CoglPipeline *pipeline;
Packit d345d1
Packit d345d1
  gfloat        width;
Packit d345d1
  gfloat        height;
Packit d345d1
};
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_helper_new:
Packit d345d1
 * @shadow: a #StShadow representing the shadow properties
Packit d345d1
 *
Packit d345d1
 * Builds a #StShadowHelper that will build a drop shadow
Packit d345d1
 * using @source as the mask.
Packit d345d1
 *
Packit d345d1
 * Returns: (transfer full): a new #StShadowHelper
Packit d345d1
 */
Packit d345d1
StShadowHelper *
Packit d345d1
st_shadow_helper_new (StShadow     *shadow)
Packit d345d1
{
Packit d345d1
  StShadowHelper *helper;
Packit d345d1
Packit d345d1
  helper = g_slice_new0 (StShadowHelper);
Packit d345d1
  helper->shadow = st_shadow_ref (shadow);
Packit d345d1
Packit d345d1
  return helper;
Packit d345d1
}
Packit d345d1
Packit d345d1
void
Packit d345d1
st_shadow_helper_update (StShadowHelper *helper,
Packit d345d1
                         ClutterActor   *source)
Packit d345d1
{
Packit d345d1
  gfloat width, height;
Packit d345d1
Packit d345d1
  clutter_actor_get_size (source, &width, &height);
Packit d345d1
Packit d345d1
  if (helper->pipeline == NULL ||
Packit d345d1
      helper->width != width ||
Packit d345d1
      helper->height != height)
Packit d345d1
    {
Packit d345d1
      if (helper->pipeline)
Packit d345d1
        cogl_object_unref (helper->pipeline);
Packit d345d1
Packit d345d1
      helper->pipeline = _st_create_shadow_pipeline_from_actor (helper->shadow, source);
Packit d345d1
      helper->width = width;
Packit d345d1
      helper->height = height;
Packit d345d1
    }
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_helper_copy:
Packit d345d1
 * @helper: the #StShadowHelper to copy
Packit d345d1
 *
Packit d345d1
 * Returns: (transfer full): a copy of @helper
Packit d345d1
 */
Packit d345d1
StShadowHelper *
Packit d345d1
st_shadow_helper_copy (StShadowHelper *helper)
Packit d345d1
{
Packit d345d1
  StShadowHelper *copy;
Packit d345d1
Packit d345d1
  copy = g_slice_new (StShadowHelper);
Packit d345d1
  *copy = *helper;
Packit d345d1
  if (copy->pipeline)
Packit d345d1
    cogl_object_ref (copy->pipeline);
Packit d345d1
  st_shadow_ref (copy->shadow);
Packit d345d1
Packit d345d1
  return copy;
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_helper_free:
Packit d345d1
 * @helper: a #StShadowHelper
Packit d345d1
 *
Packit d345d1
 * Free resources associated with @helper.
Packit d345d1
 */
Packit d345d1
void
Packit d345d1
st_shadow_helper_free (StShadowHelper *helper)
Packit d345d1
{
Packit d345d1
  if (helper->pipeline)
Packit d345d1
    cogl_object_unref (helper->pipeline);
Packit d345d1
  st_shadow_unref (helper->shadow);
Packit d345d1
Packit d345d1
  g_slice_free (StShadowHelper, helper);
Packit d345d1
}
Packit d345d1
Packit d345d1
/**
Packit d345d1
 * st_shadow_helper_paint:
Packit d345d1
 * @helper: a #StShadowHelper
Packit d345d1
 * @framebuffer: a #CoglFramebuffer
Packit d345d1
 * @actor_box: the bounding box of the shadow
Packit d345d1
 * @paint_opacity: the opacity at which the shadow is painted
Packit d345d1
 *
Packit d345d1
 * Paints the shadow associated with @helper This must only
Packit d345d1
 * be called from the implementation of ClutterActor::paint().
Packit d345d1
 */
Packit d345d1
void
Packit d345d1
st_shadow_helper_paint (StShadowHelper  *helper,
Packit d345d1
                        CoglFramebuffer *framebuffer,
Packit d345d1
                        ClutterActorBox *actor_box,
Packit d345d1
                        guint8           paint_opacity)
Packit d345d1
{
Packit d345d1
  _st_paint_shadow_with_opacity (helper->shadow,
Packit d345d1
                                 framebuffer,
Packit d345d1
                                 helper->pipeline,
Packit d345d1
                                 actor_box,
Packit d345d1
                                 paint_opacity);
Packit d345d1
}