Blame modules/engines/pixbuf/pixbuf-render.c

Packit 98cdb6
/* GTK+ Pixbuf Engine
Packit 98cdb6
 * Copyright (C) 1998-2000 Red Hat, Inc.
Packit 98cdb6
 *
Packit 98cdb6
 * This library is free software; you can redistribute it and/or
Packit 98cdb6
 * modify it under the terms of the GNU Library General Public
Packit 98cdb6
 * License as published by the Free Software Foundation; either
Packit 98cdb6
 * version 2 of the License, or (at your option) any later version.
Packit 98cdb6
 *
Packit 98cdb6
 * This library is distributed in the hope that it will be useful,
Packit 98cdb6
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 98cdb6
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 98cdb6
 * Library General Public License for more details.
Packit 98cdb6
 *
Packit 98cdb6
 * You should have received a copy of the GNU Library General Public
Packit 98cdb6
 * License along with this library; if not, write to the
Packit 98cdb6
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Packit 98cdb6
 * Boston, MA 02111-1307, USA.
Packit 98cdb6
 *
Packit 98cdb6
 * Written by Owen Taylor <otaylor@redhat.com>, based on code by
Packit 98cdb6
 * Carsten Haitzler <raster@rasterman.com>
Packit 98cdb6
 */
Packit 98cdb6
Packit 98cdb6
#include <string.h>
Packit 98cdb6
Packit 98cdb6
#include "pixbuf.h"
Packit 98cdb6
#include <gdk-pixbuf/gdk-pixbuf.h>
Packit 98cdb6
Packit 98cdb6
static GCache *pixbuf_cache = NULL;
Packit 98cdb6
Packit 98cdb6
static GdkPixbuf *
Packit 98cdb6
bilinear_gradient (GdkPixbuf    *src,
Packit 98cdb6
		   gint          src_x,
Packit 98cdb6
		   gint          src_y,
Packit 98cdb6
		   gint          width,
Packit 98cdb6
		   gint          height)
Packit 98cdb6
{
Packit 98cdb6
  guint n_channels = gdk_pixbuf_get_n_channels (src);
Packit 98cdb6
  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
Packit 98cdb6
  guchar *src_pixels = gdk_pixbuf_get_pixels (src);
Packit 98cdb6
  guchar *p1, *p2, *p3, *p4;
Packit 98cdb6
  guint dest_rowstride;
Packit 98cdb6
  guchar *dest_pixels;
Packit 98cdb6
  GdkPixbuf *result;
Packit 98cdb6
  int i, j, k;
Packit 98cdb6
Packit 98cdb6
  if (src_x == 0 || src_y == 0)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("invalid source position for bilinear gradient");
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  p1 = src_pixels + (src_y - 1) * src_rowstride + (src_x - 1) * n_channels;
Packit 98cdb6
  p2 = p1 + n_channels;
Packit 98cdb6
  p3 = src_pixels + src_y * src_rowstride + (src_x - 1) * n_channels;
Packit 98cdb6
  p4 = p3 + n_channels;
Packit 98cdb6
Packit 98cdb6
  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
Packit 98cdb6
			   width, height);
Packit 98cdb6
Packit 98cdb6
  if (result == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("failed to create a %dx%d pixbuf", width, height);
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  dest_rowstride = gdk_pixbuf_get_rowstride (result);
Packit 98cdb6
  dest_pixels = gdk_pixbuf_get_pixels (result);
Packit 98cdb6
Packit 98cdb6
  for (i = 0; i < height; i++)
Packit 98cdb6
    {
Packit 98cdb6
      guchar *p = dest_pixels + dest_rowstride *i;
Packit 98cdb6
      guint v[4];
Packit 98cdb6
      gint dv[4];
Packit 98cdb6
Packit 98cdb6
      for (k = 0; k < n_channels; k++)
Packit 98cdb6
	{
Packit 98cdb6
	  guint start = ((height - i) * p1[k] + (1 + i) * p3[k]) / (height + 1);
Packit 98cdb6
	  guint end = ((height -  i) * p2[k] + (1 + i) * p4[k]) / (height + 1);
Packit 98cdb6
Packit 98cdb6
	  dv[k] = (((gint)end - (gint)start) << 16) / (width + 1);
Packit 98cdb6
	  v[k] = (start << 16) + dv[k] + 0x8000;
Packit 98cdb6
	}
Packit 98cdb6
Packit 98cdb6
      for (j = width; j; j--)
Packit 98cdb6
	{
Packit 98cdb6
	  for (k = 0; k < n_channels; k++)
Packit 98cdb6
	    {
Packit 98cdb6
	      *(p++) = v[k] >> 16;
Packit 98cdb6
	      v[k] += dv[k];
Packit 98cdb6
	    }
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static GdkPixbuf *
Packit 98cdb6
horizontal_gradient (GdkPixbuf    *src,
Packit 98cdb6
		     gint          src_x,
Packit 98cdb6
		     gint          src_y,
Packit 98cdb6
		     gint          width,
Packit 98cdb6
		     gint          height)
Packit 98cdb6
{
Packit 98cdb6
  guint n_channels = gdk_pixbuf_get_n_channels (src);
Packit 98cdb6
  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
Packit 98cdb6
  guchar *src_pixels = gdk_pixbuf_get_pixels (src);
Packit 98cdb6
  guint dest_rowstride;
Packit 98cdb6
  guchar *dest_pixels;
Packit 98cdb6
  GdkPixbuf *result;
Packit 98cdb6
  int i, j, k;
Packit 98cdb6
Packit 98cdb6
  if (src_x == 0)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("invalid source position for horizontal gradient");
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
Packit 98cdb6
			   width, height);
Packit 98cdb6
Packit 98cdb6
  if (result == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("failed to create a %dx%d pixbuf", width, height);
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  dest_rowstride = gdk_pixbuf_get_rowstride (result);
Packit 98cdb6
  dest_pixels = gdk_pixbuf_get_pixels (result);
Packit 98cdb6
Packit 98cdb6
  for (i = 0; i < height; i++)
Packit 98cdb6
    {
Packit 98cdb6
      guchar *p = dest_pixels + dest_rowstride *i;
Packit 98cdb6
      guchar *p1 = src_pixels + (src_y + i) * src_rowstride + (src_x - 1) * n_channels;
Packit 98cdb6
      guchar *p2 = p1 + n_channels;
Packit 98cdb6
Packit 98cdb6
      guint v[4];
Packit 98cdb6
      gint dv[4];
Packit 98cdb6
Packit 98cdb6
      for (k = 0; k < n_channels; k++)
Packit 98cdb6
	{
Packit 98cdb6
	  dv[k] = (((gint)p2[k] - (gint)p1[k]) << 16) / (width + 1);
Packit 98cdb6
	  v[k] = (p1[k] << 16) + dv[k] + 0x8000;
Packit 98cdb6
	}
Packit 98cdb6
      
Packit 98cdb6
      for (j = width; j; j--)
Packit 98cdb6
	{
Packit 98cdb6
	  for (k = 0; k < n_channels; k++)
Packit 98cdb6
	    {
Packit 98cdb6
	      *(p++) = v[k] >> 16;
Packit 98cdb6
	      v[k] += dv[k];
Packit 98cdb6
	    }
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static GdkPixbuf *
Packit 98cdb6
vertical_gradient (GdkPixbuf    *src,
Packit 98cdb6
		   gint          src_x,
Packit 98cdb6
		   gint          src_y,
Packit 98cdb6
		   gint          width,
Packit 98cdb6
		   gint          height)
Packit 98cdb6
{
Packit 98cdb6
  guint n_channels = gdk_pixbuf_get_n_channels (src);
Packit 98cdb6
  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
Packit 98cdb6
  guchar *src_pixels = gdk_pixbuf_get_pixels (src);
Packit 98cdb6
  guchar *top_pixels, *bottom_pixels;
Packit 98cdb6
  guint dest_rowstride;
Packit 98cdb6
  guchar *dest_pixels;
Packit 98cdb6
  GdkPixbuf *result;
Packit 98cdb6
  int i, j;
Packit 98cdb6
Packit 98cdb6
  if (src_y == 0)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("invalid source position for vertical gradient");
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  top_pixels = src_pixels + (src_y - 1) * src_rowstride + (src_x) * n_channels;
Packit 98cdb6
  bottom_pixels = top_pixels + src_rowstride;
Packit 98cdb6
Packit 98cdb6
  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
Packit 98cdb6
			   width, height);
Packit 98cdb6
Packit 98cdb6
  if (result == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("failed to create a %dx%d pixbuf", width, height);
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  dest_rowstride = gdk_pixbuf_get_rowstride (result);
Packit 98cdb6
  dest_pixels = gdk_pixbuf_get_pixels (result);
Packit 98cdb6
Packit 98cdb6
  for (i = 0; i < height; i++)
Packit 98cdb6
    {
Packit 98cdb6
      guchar *p = dest_pixels + dest_rowstride *i;
Packit 98cdb6
      guchar *p1 = top_pixels;
Packit 98cdb6
      guchar *p2 = bottom_pixels;
Packit 98cdb6
Packit 98cdb6
      for (j = width * n_channels; j; j--)
Packit 98cdb6
	*(p++) = ((height - i) * *(p1++) + (1 + i) * *(p2++)) / (height + 1);
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static GdkPixbuf *
Packit 98cdb6
replicate_single (GdkPixbuf    *src,
Packit 98cdb6
		  gint          src_x,
Packit 98cdb6
		  gint          src_y,
Packit 98cdb6
		  gint          width,
Packit 98cdb6
		  gint          height)
Packit 98cdb6
{
Packit 98cdb6
  guint n_channels = gdk_pixbuf_get_n_channels (src);
Packit 98cdb6
  guchar *pixels = (gdk_pixbuf_get_pixels (src) +
Packit 98cdb6
		    src_y * gdk_pixbuf_get_rowstride (src) +
Packit 98cdb6
		    src_x * n_channels);
Packit 98cdb6
  guchar r = *(pixels++);
Packit 98cdb6
  guchar g = *(pixels++);
Packit 98cdb6
  guchar b = *(pixels++);
Packit 98cdb6
  guint dest_rowstride;
Packit 98cdb6
  guchar *dest_pixels;
Packit 98cdb6
  guchar a = 0;
Packit 98cdb6
  GdkPixbuf *result;
Packit 98cdb6
  int i, j;
Packit 98cdb6
Packit 98cdb6
  if (n_channels == 4)
Packit 98cdb6
    a = *(pixels++);
Packit 98cdb6
Packit 98cdb6
  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
Packit 98cdb6
			   width, height);
Packit 98cdb6
Packit 98cdb6
  if (result == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("failed to create a %dx%d pixbuf", width, height);
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  dest_rowstride = gdk_pixbuf_get_rowstride (result);
Packit 98cdb6
  dest_pixels = gdk_pixbuf_get_pixels (result);
Packit 98cdb6
  
Packit 98cdb6
  for (i = 0; i < height; i++)
Packit 98cdb6
    {
Packit 98cdb6
      guchar *p = dest_pixels + dest_rowstride *i;
Packit 98cdb6
Packit 98cdb6
      for (j = 0; j < width; j++)
Packit 98cdb6
	{
Packit 98cdb6
	  *(p++) = r;
Packit 98cdb6
	  *(p++) = g;
Packit 98cdb6
	  *(p++) = b;
Packit 98cdb6
Packit 98cdb6
	  if (n_channels == 4)
Packit 98cdb6
	    *(p++) = a;
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static GdkPixbuf *
Packit 98cdb6
replicate_rows (GdkPixbuf    *src,
Packit 98cdb6
		gint          src_x,
Packit 98cdb6
		gint          src_y,
Packit 98cdb6
		gint          width,
Packit 98cdb6
		gint          height)
Packit 98cdb6
{
Packit 98cdb6
  guint n_channels = gdk_pixbuf_get_n_channels (src);
Packit 98cdb6
  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
Packit 98cdb6
  guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
Packit 98cdb6
  guchar *dest_pixels;
Packit 98cdb6
  GdkPixbuf *result;
Packit 98cdb6
  guint dest_rowstride;
Packit 98cdb6
  int i;
Packit 98cdb6
Packit 98cdb6
  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
Packit 98cdb6
			   width, height);
Packit 98cdb6
Packit 98cdb6
  if (result == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("failed to create a %dx%d pixbuf", width, height);
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  dest_rowstride = gdk_pixbuf_get_rowstride (result);
Packit 98cdb6
  dest_pixels = gdk_pixbuf_get_pixels (result);
Packit 98cdb6
Packit 98cdb6
  for (i = 0; i < height; i++)
Packit 98cdb6
    memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static GdkPixbuf *
Packit 98cdb6
replicate_cols (GdkPixbuf    *src,
Packit 98cdb6
		gint          src_x,
Packit 98cdb6
		gint          src_y,
Packit 98cdb6
		gint          width,
Packit 98cdb6
		gint          height)
Packit 98cdb6
{
Packit 98cdb6
  guint n_channels = gdk_pixbuf_get_n_channels (src);
Packit 98cdb6
  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
Packit 98cdb6
  guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
Packit 98cdb6
  guchar *dest_pixels;
Packit 98cdb6
  GdkPixbuf *result;
Packit 98cdb6
  guint dest_rowstride;
Packit 98cdb6
  int i, j;
Packit 98cdb6
Packit 98cdb6
  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
Packit 98cdb6
			   width, height);
Packit 98cdb6
Packit 98cdb6
  if (result == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("failed to create a %dx%d pixbuf", width, height);
Packit 98cdb6
      return NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  dest_rowstride = gdk_pixbuf_get_rowstride (result);
Packit 98cdb6
  dest_pixels = gdk_pixbuf_get_pixels (result);
Packit 98cdb6
Packit 98cdb6
  for (i = 0; i < height; i++)
Packit 98cdb6
    {
Packit 98cdb6
      guchar *p = dest_pixels + dest_rowstride * i;
Packit 98cdb6
      guchar *q = pixels + src_rowstride * i;
Packit 98cdb6
Packit 98cdb6
      guchar r = *(q++);
Packit 98cdb6
      guchar g = *(q++);
Packit 98cdb6
      guchar b = *(q++);
Packit 98cdb6
      guchar a = 0;
Packit 98cdb6
      
Packit 98cdb6
      if (n_channels == 4)
Packit 98cdb6
	a = *(q++);
Packit 98cdb6
Packit 98cdb6
      for (j = 0; j < width; j++)
Packit 98cdb6
	{
Packit 98cdb6
	  *(p++) = r;
Packit 98cdb6
	  *(p++) = g;
Packit 98cdb6
	  *(p++) = b;
Packit 98cdb6
Packit 98cdb6
	  if (n_channels == 4)
Packit 98cdb6
	    *(p++) = a;
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
/* Scale the rectangle (src_x, src_y, src_width, src_height)
Packit 98cdb6
 * onto the rectangle (dest_x, dest_y, dest_width, dest_height)
Packit 98cdb6
 * of the destination, clip by clip_rect and render
Packit 98cdb6
 */
Packit 98cdb6
static void
Packit 98cdb6
pixbuf_render (GdkPixbuf    *src,
Packit 98cdb6
	       guint         hints,
Packit 98cdb6
	       GdkWindow    *window,
Packit 98cdb6
	       GdkBitmap    *mask,
Packit 98cdb6
	       GdkRectangle *clip_rect,
Packit 98cdb6
	       gint          src_x,
Packit 98cdb6
	       gint          src_y,
Packit 98cdb6
	       gint          src_width,
Packit 98cdb6
	       gint          src_height,
Packit 98cdb6
	       gint          dest_x,
Packit 98cdb6
	       gint          dest_y,
Packit 98cdb6
	       gint          dest_width,
Packit 98cdb6
	       gint          dest_height)
Packit 98cdb6
{
Packit 98cdb6
  GdkPixbuf *tmp_pixbuf = NULL;
Packit 98cdb6
  GdkRectangle rect;
Packit 98cdb6
  int x_offset, y_offset;
Packit 98cdb6
  gboolean has_alpha = gdk_pixbuf_get_has_alpha (src);
Packit 98cdb6
  gint src_rowstride = gdk_pixbuf_get_rowstride (src);
Packit 98cdb6
  gint src_n_channels = gdk_pixbuf_get_n_channels (src);
Packit 98cdb6
Packit 98cdb6
  if (dest_width <= 0 || dest_height <= 0)
Packit 98cdb6
    return;
Packit 98cdb6
Packit 98cdb6
  rect.x = dest_x;
Packit 98cdb6
  rect.y = dest_y;
Packit 98cdb6
  rect.width = dest_width;
Packit 98cdb6
  rect.height = dest_height;
Packit 98cdb6
Packit 98cdb6
  if (hints & THEME_MISSING)
Packit 98cdb6
    return;
Packit 98cdb6
Packit 98cdb6
  /* FIXME: Because we use the mask to shape windows, we don't use
Packit 98cdb6
   * clip_rect to clip what we draw to the mask, only to clip
Packit 98cdb6
   * what we actually draw. But this leads to the horrible ineffiency
Packit 98cdb6
   * of scale the whole image to get a little bit of it.
Packit 98cdb6
   */
Packit 98cdb6
  if (!mask && clip_rect)
Packit 98cdb6
    {
Packit 98cdb6
      if (!gdk_rectangle_intersect (clip_rect, &rect, &rect))
Packit 98cdb6
	return;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  if (dest_width == src_width && dest_height == src_height)
Packit 98cdb6
    {
Packit 98cdb6
      tmp_pixbuf = g_object_ref (src);
Packit 98cdb6
Packit 98cdb6
      x_offset = src_x + rect.x - dest_x;
Packit 98cdb6
      y_offset = src_y + rect.y - dest_y;
Packit 98cdb6
    }
Packit 98cdb6
  else if (src_width == 0 && src_height == 0)
Packit 98cdb6
    {
Packit 98cdb6
      tmp_pixbuf = bilinear_gradient (src, src_x, src_y, dest_width, dest_height);      
Packit 98cdb6
      
Packit 98cdb6
      x_offset = rect.x - dest_x;
Packit 98cdb6
      y_offset = rect.y - dest_y;
Packit 98cdb6
    }
Packit 98cdb6
  else if (src_width == 0 && dest_height == src_height)
Packit 98cdb6
    {
Packit 98cdb6
      tmp_pixbuf = horizontal_gradient (src, src_x, src_y, dest_width, dest_height);      
Packit 98cdb6
      
Packit 98cdb6
      x_offset = rect.x - dest_x;
Packit 98cdb6
      y_offset = rect.y - dest_y;
Packit 98cdb6
    }
Packit 98cdb6
  else if (src_height == 0 && dest_width == src_width)
Packit 98cdb6
    {
Packit 98cdb6
      tmp_pixbuf = vertical_gradient (src, src_x, src_y, dest_width, dest_height);
Packit 98cdb6
      
Packit 98cdb6
      x_offset = rect.x - dest_x;
Packit 98cdb6
      y_offset = rect.y - dest_y;
Packit 98cdb6
    }
Packit 98cdb6
  else if ((hints & THEME_CONSTANT_COLS) && (hints & THEME_CONSTANT_ROWS))
Packit 98cdb6
    {
Packit 98cdb6
      tmp_pixbuf = replicate_single (src, src_x, src_y, dest_width, dest_height);
Packit 98cdb6
Packit 98cdb6
      x_offset = rect.x - dest_x;
Packit 98cdb6
      y_offset = rect.y - dest_y;
Packit 98cdb6
    }
Packit 98cdb6
  else if (dest_width == src_width && (hints & THEME_CONSTANT_COLS))
Packit 98cdb6
    {
Packit 98cdb6
      tmp_pixbuf = replicate_rows (src, src_x, src_y, dest_width, dest_height);
Packit 98cdb6
Packit 98cdb6
      x_offset = rect.x - dest_x;
Packit 98cdb6
      y_offset = rect.y - dest_y;
Packit 98cdb6
    }
Packit 98cdb6
  else if (dest_height == src_height && (hints & THEME_CONSTANT_ROWS))
Packit 98cdb6
    {
Packit 98cdb6
      tmp_pixbuf = replicate_cols (src, src_x, src_y, dest_width, dest_height);
Packit 98cdb6
Packit 98cdb6
      x_offset = rect.x - dest_x;
Packit 98cdb6
      y_offset = rect.y - dest_y;
Packit 98cdb6
    }
Packit 98cdb6
  else if (src_width > 0 && src_height > 0)
Packit 98cdb6
    {
Packit 98cdb6
      double x_scale = (double)dest_width / src_width;
Packit 98cdb6
      double y_scale = (double)dest_height / src_height;
Packit 98cdb6
      guchar *pixels;
Packit 98cdb6
      GdkPixbuf *partial_src;
Packit 98cdb6
      
Packit 98cdb6
      pixels = (gdk_pixbuf_get_pixels (src)
Packit 98cdb6
		+ src_y * src_rowstride
Packit 98cdb6
		+ src_x * src_n_channels);
Packit 98cdb6
Packit 98cdb6
      partial_src = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB,
Packit 98cdb6
					      has_alpha,
Packit 98cdb6
					      8, src_width, src_height,
Packit 98cdb6
					      src_rowstride,
Packit 98cdb6
					      NULL, NULL);
Packit 98cdb6
						  
Packit 98cdb6
      tmp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
Packit 98cdb6
				   has_alpha, 8,
Packit 98cdb6
				   rect.width, rect.height);
Packit 98cdb6
Packit 98cdb6
      gdk_pixbuf_scale (partial_src, tmp_pixbuf,
Packit 98cdb6
			0, 0, rect.width, rect.height,
Packit 98cdb6
			dest_x - rect.x, dest_y - rect.y, 
Packit 98cdb6
			x_scale, y_scale,
Packit 98cdb6
			GDK_INTERP_BILINEAR);
Packit 98cdb6
Packit 98cdb6
      g_object_unref (partial_src);
Packit 98cdb6
Packit 98cdb6
      x_offset = 0;
Packit 98cdb6
      y_offset = 0;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  if (tmp_pixbuf)
Packit 98cdb6
    {
Packit 98cdb6
      cairo_t *cr;
Packit 98cdb6
      
Packit 98cdb6
      if (mask)
Packit 98cdb6
	{
Packit 98cdb6
          cr = gdk_cairo_create (mask);
Packit 98cdb6
Packit 98cdb6
          gdk_cairo_set_source_pixbuf (cr, tmp_pixbuf,
Packit 98cdb6
                                       -x_offset + rect.x, 
Packit 98cdb6
                                       -y_offset + rect.y);
Packit 98cdb6
          gdk_cairo_rectangle (cr, &rect);
Packit 98cdb6
          cairo_fill (cr);
Packit 98cdb6
Packit 98cdb6
          cairo_destroy (cr);
Packit 98cdb6
	}
Packit 98cdb6
Packit 98cdb6
      cr = gdk_cairo_create (window);
Packit 98cdb6
      gdk_cairo_set_source_pixbuf (cr, 
Packit 98cdb6
                                   tmp_pixbuf,
Packit 98cdb6
                                   -x_offset + rect.x, 
Packit 98cdb6
                                   -y_offset + rect.y);
Packit 98cdb6
      gdk_cairo_rectangle (cr, &rect);
Packit 98cdb6
      cairo_fill (cr);
Packit 98cdb6
Packit 98cdb6
      cairo_destroy (cr);
Packit 98cdb6
      g_object_unref (tmp_pixbuf);
Packit 98cdb6
    }
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
ThemePixbuf *
Packit 98cdb6
theme_pixbuf_new (void)
Packit 98cdb6
{
Packit 98cdb6
  ThemePixbuf *result = g_new0 (ThemePixbuf, 1);
Packit 98cdb6
  result->filename = NULL;
Packit 98cdb6
  result->pixbuf = NULL;
Packit 98cdb6
Packit 98cdb6
  result->stretch = TRUE;
Packit 98cdb6
  result->border_left = 0;
Packit 98cdb6
  result->border_right = 0;
Packit 98cdb6
  result->border_bottom = 0;
Packit 98cdb6
  result->border_top = 0;
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
void
Packit 98cdb6
theme_pixbuf_destroy (ThemePixbuf *theme_pb)
Packit 98cdb6
{
Packit 98cdb6
  if (G_LIKELY (theme_pb))
Packit 98cdb6
    {
Packit 98cdb6
      theme_pixbuf_set_filename (theme_pb, NULL);
Packit 98cdb6
      g_free (theme_pb);
Packit 98cdb6
    }
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
void
Packit 98cdb6
theme_clear_pixbuf (ThemePixbuf **theme_pb)
Packit 98cdb6
{
Packit 98cdb6
#if GLIB_CHECK_VERSION (2, 34, 0)
Packit 98cdb6
  g_clear_pointer (theme_pb, theme_pixbuf_destroy);
Packit 98cdb6
#else
Packit 98cdb6
  if (*theme_pb)
Packit 98cdb6
    {
Packit 98cdb6
      theme_pixbuf_destroy (*theme_pb);
Packit 98cdb6
      *theme_pb = NULL;
Packit 98cdb6
    }
Packit 98cdb6
#endif
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
void         
Packit 98cdb6
theme_pixbuf_set_filename (ThemePixbuf *theme_pb,
Packit 98cdb6
			   const char  *filename)
Packit 98cdb6
{
Packit 98cdb6
  if (theme_pb->pixbuf)
Packit 98cdb6
    {
Packit 98cdb6
      g_cache_remove (pixbuf_cache, theme_pb->pixbuf);
Packit 98cdb6
      theme_pb->pixbuf = NULL;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  g_free (theme_pb->filename);
Packit 98cdb6
Packit 98cdb6
  if (filename)
Packit 98cdb6
    theme_pb->filename = g_strdup (filename);
Packit 98cdb6
  else
Packit 98cdb6
    theme_pb->filename = NULL;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static guint
Packit 98cdb6
compute_hint (GdkPixbuf *pixbuf,
Packit 98cdb6
	      gint       x0,
Packit 98cdb6
	      gint       x1,
Packit 98cdb6
	      gint       y0,
Packit 98cdb6
	      gint       y1)
Packit 98cdb6
{
Packit 98cdb6
  int i, j;
Packit 98cdb6
  int hints = THEME_CONSTANT_ROWS | THEME_CONSTANT_COLS | THEME_MISSING;
Packit 98cdb6
  int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
Packit 98cdb6
  
Packit 98cdb6
  guchar *data = gdk_pixbuf_get_pixels (pixbuf);
Packit 98cdb6
  int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
Packit 98cdb6
Packit 98cdb6
  if (x0 == x1 || y0 == y1)
Packit 98cdb6
    return 0;
Packit 98cdb6
Packit 98cdb6
  for (i = y0; i < y1; i++)
Packit 98cdb6
    {
Packit 98cdb6
      guchar *p = data + i * rowstride + x0 * n_channels;
Packit 98cdb6
      guchar r = p[0];
Packit 98cdb6
      guchar g = p[1];
Packit 98cdb6
      guchar b = p[2];
Packit 98cdb6
      guchar a = 0;
Packit 98cdb6
      
Packit 98cdb6
      if (n_channels == 4)
Packit 98cdb6
	a = p[3];
Packit 98cdb6
Packit 98cdb6
      for (j = x0; j < x1 ; j++)
Packit 98cdb6
	{
Packit 98cdb6
	  if (n_channels != 4 || p[3] != 0)
Packit 98cdb6
	    {
Packit 98cdb6
	      hints &= ~THEME_MISSING;
Packit 98cdb6
	      if (!(hints & THEME_CONSTANT_ROWS))
Packit 98cdb6
		goto cols;
Packit 98cdb6
	    }
Packit 98cdb6
	  
Packit 98cdb6
	  if (r != *(p++) ||
Packit 98cdb6
	      g != *(p++) ||
Packit 98cdb6
	      b != *(p++) ||
Packit 98cdb6
	      (n_channels != 4 && a != *(p++)))
Packit 98cdb6
	    {
Packit 98cdb6
	      hints &= ~THEME_CONSTANT_ROWS;
Packit 98cdb6
	      if (!(hints & THEME_MISSING))
Packit 98cdb6
		goto cols;
Packit 98cdb6
	    }
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
 cols:
Packit 98cdb6
  for (i = y0 + 1; i < y1; i++)
Packit 98cdb6
    {
Packit 98cdb6
      guchar *base = data + y0 * rowstride + x0 * n_channels;
Packit 98cdb6
      guchar *p = data + i * rowstride + x0 * n_channels;
Packit 98cdb6
Packit 98cdb6
      if (memcmp (p, base, n_channels * (x1 - x0)) != 0)
Packit 98cdb6
	{
Packit 98cdb6
	  hints &= ~THEME_CONSTANT_COLS;
Packit 98cdb6
	  return hints;
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return hints;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static void
Packit 98cdb6
theme_pixbuf_compute_hints (ThemePixbuf *theme_pb)
Packit 98cdb6
{
Packit 98cdb6
  int i, j;
Packit 98cdb6
  gint width = gdk_pixbuf_get_width (theme_pb->pixbuf);
Packit 98cdb6
  gint height = gdk_pixbuf_get_height (theme_pb->pixbuf);
Packit 98cdb6
Packit 98cdb6
  if (theme_pb->border_left + theme_pb->border_right > width ||
Packit 98cdb6
      theme_pb->border_top + theme_pb->border_bottom > height)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("Invalid borders specified for theme pixmap:\n"
Packit 98cdb6
		 "        %s,\n"
Packit 98cdb6
		 "borders don't fit within the image", theme_pb->filename);
Packit 98cdb6
      if (theme_pb->border_left + theme_pb->border_right > width)
Packit 98cdb6
	{
Packit 98cdb6
	  theme_pb->border_left = width / 2;
Packit 98cdb6
	  theme_pb->border_right = (width + 1) / 2;
Packit 98cdb6
	}
Packit 98cdb6
      if (theme_pb->border_bottom + theme_pb->border_top > height)
Packit 98cdb6
	{
Packit 98cdb6
	  theme_pb->border_top = height / 2;
Packit 98cdb6
	  theme_pb->border_bottom = (height + 1) / 2;
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
  
Packit 98cdb6
  for (i = 0; i < 3; i++)
Packit 98cdb6
    {
Packit 98cdb6
      gint y0, y1;
Packit 98cdb6
Packit 98cdb6
      switch (i)
Packit 98cdb6
	{
Packit 98cdb6
	case 0:
Packit 98cdb6
	  y0 = 0;
Packit 98cdb6
	  y1 = theme_pb->border_top;
Packit 98cdb6
	  break;
Packit 98cdb6
	case 1:
Packit 98cdb6
	  y0 = theme_pb->border_top;
Packit 98cdb6
	  y1 = height - theme_pb->border_bottom;
Packit 98cdb6
	  break;
Packit 98cdb6
	default:
Packit 98cdb6
	  y0 = height - theme_pb->border_bottom;
Packit 98cdb6
	  y1 = height;
Packit 98cdb6
	  break;
Packit 98cdb6
	}
Packit 98cdb6
      
Packit 98cdb6
      for (j = 0; j < 3; j++)
Packit 98cdb6
	{
Packit 98cdb6
	  gint x0, x1;
Packit 98cdb6
	  
Packit 98cdb6
	  switch (j)
Packit 98cdb6
	    {
Packit 98cdb6
	    case 0:
Packit 98cdb6
	      x0 = 0;
Packit 98cdb6
	      x1 = theme_pb->border_left;
Packit 98cdb6
	      break;
Packit 98cdb6
	    case 1:
Packit 98cdb6
	      x0 = theme_pb->border_left;
Packit 98cdb6
	      x1 = width - theme_pb->border_right;
Packit 98cdb6
	      break;
Packit 98cdb6
	    default:
Packit 98cdb6
	      x0 = width - theme_pb->border_right;
Packit 98cdb6
	      x1 = width;
Packit 98cdb6
	      break;
Packit 98cdb6
	    }
Packit 98cdb6
Packit 98cdb6
	  theme_pb->hints[i][j] = compute_hint (theme_pb->pixbuf, x0, x1, y0, y1);
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
  
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
void
Packit 98cdb6
theme_pixbuf_set_border (ThemePixbuf *theme_pb,
Packit 98cdb6
			 gint         left,
Packit 98cdb6
			 gint         right,
Packit 98cdb6
			 gint         top,
Packit 98cdb6
			 gint         bottom)
Packit 98cdb6
{
Packit 98cdb6
  theme_pb->border_left = left;
Packit 98cdb6
  theme_pb->border_right = right;
Packit 98cdb6
  theme_pb->border_top = top;
Packit 98cdb6
  theme_pb->border_bottom = bottom;
Packit 98cdb6
Packit 98cdb6
  if (theme_pb->pixbuf)
Packit 98cdb6
    theme_pixbuf_compute_hints (theme_pb);
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
void
Packit 98cdb6
theme_pixbuf_set_stretch (ThemePixbuf *theme_pb,
Packit 98cdb6
			  gboolean     stretch)
Packit 98cdb6
{
Packit 98cdb6
  theme_pb->stretch = stretch;
Packit 98cdb6
Packit 98cdb6
  if (theme_pb->pixbuf)
Packit 98cdb6
    theme_pixbuf_compute_hints (theme_pb);
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static GdkPixbuf *
Packit 98cdb6
pixbuf_cache_value_new (gchar *filename)
Packit 98cdb6
{
Packit 98cdb6
  GError *err = NULL;
Packit 98cdb6
    
Packit 98cdb6
  GdkPixbuf *result = gdk_pixbuf_new_from_file (filename, &err;;
Packit 98cdb6
  if (!result)
Packit 98cdb6
    {
Packit 98cdb6
      g_warning ("Pixbuf theme: Cannot load pixmap file %s: %s\n",
Packit 98cdb6
		 filename, err->message);
Packit 98cdb6
      g_error_free (err);
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return result;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
GdkPixbuf *
Packit 98cdb6
theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb)
Packit 98cdb6
{
Packit 98cdb6
  if (!theme_pb->pixbuf)
Packit 98cdb6
    {
Packit 98cdb6
      if (!pixbuf_cache)
Packit 98cdb6
	pixbuf_cache = g_cache_new ((GCacheNewFunc)pixbuf_cache_value_new,
Packit 98cdb6
				    (GCacheDestroyFunc)g_object_unref,
Packit 98cdb6
				    (GCacheDupFunc)g_strdup,
Packit 98cdb6
				    (GCacheDestroyFunc)g_free,
Packit 98cdb6
				    g_str_hash, g_direct_hash, g_str_equal);
Packit 98cdb6
      
Packit 98cdb6
      theme_pb->pixbuf = g_cache_insert (pixbuf_cache, theme_pb->filename);
Packit 98cdb6
Packit 98cdb6
      if (theme_pb->stretch)
Packit 98cdb6
	theme_pixbuf_compute_hints (theme_pb);
Packit 98cdb6
    }
Packit 98cdb6
  
Packit 98cdb6
  return theme_pb->pixbuf;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
void
Packit 98cdb6
theme_pixbuf_render (ThemePixbuf  *theme_pb,
Packit 98cdb6
		     GdkWindow    *window,
Packit 98cdb6
		     GdkBitmap    *mask,
Packit 98cdb6
		     GdkRectangle *clip_rect,
Packit 98cdb6
		     guint         component_mask,
Packit 98cdb6
		     gboolean      center,
Packit 98cdb6
		     gint          x,
Packit 98cdb6
		     gint          y,
Packit 98cdb6
		     gint          width,
Packit 98cdb6
		     gint          height)
Packit 98cdb6
{
Packit 98cdb6
  GdkPixbuf *pixbuf = theme_pixbuf_get_pixbuf (theme_pb);
Packit 98cdb6
  gint src_x[4], src_y[4], dest_x[4], dest_y[4];
Packit 98cdb6
  gint pixbuf_width = gdk_pixbuf_get_width (pixbuf);
Packit 98cdb6
  gint pixbuf_height = gdk_pixbuf_get_height (pixbuf);
Packit 98cdb6
Packit 98cdb6
  if (!pixbuf)
Packit 98cdb6
    return;
Packit 98cdb6
Packit 98cdb6
  if (theme_pb->stretch)
Packit 98cdb6
    {
Packit 98cdb6
      if (component_mask & COMPONENT_ALL)
Packit 98cdb6
	component_mask = (COMPONENT_ALL - 1) & ~component_mask;
Packit 98cdb6
Packit 98cdb6
      src_x[0] = 0;
Packit 98cdb6
      src_x[1] = theme_pb->border_left;
Packit 98cdb6
      src_x[2] = pixbuf_width - theme_pb->border_right;
Packit 98cdb6
      src_x[3] = pixbuf_width;
Packit 98cdb6
      
Packit 98cdb6
      src_y[0] = 0;
Packit 98cdb6
      src_y[1] = theme_pb->border_top;
Packit 98cdb6
      src_y[2] = pixbuf_height - theme_pb->border_bottom;
Packit 98cdb6
      src_y[3] = pixbuf_height;
Packit 98cdb6
      
Packit 98cdb6
      dest_x[0] = x;
Packit 98cdb6
      dest_x[1] = x + theme_pb->border_left;
Packit 98cdb6
      dest_x[2] = x + width - theme_pb->border_right;
Packit 98cdb6
      dest_x[3] = x + width;
Packit 98cdb6
Packit 98cdb6
      if (dest_x[1] > dest_x[2])
Packit 98cdb6
	{
Packit 98cdb6
	  component_mask &= ~(COMPONENT_NORTH | COMPONENT_SOUTH | COMPONENT_CENTER);
Packit 98cdb6
	  dest_x[1] = dest_x[2] = (dest_x[1] + dest_x[2]) / 2;
Packit 98cdb6
	}
Packit 98cdb6
Packit 98cdb6
      dest_y[0] = y;
Packit 98cdb6
      dest_y[1] = y + theme_pb->border_top;
Packit 98cdb6
      dest_y[2] = y + height - theme_pb->border_bottom;
Packit 98cdb6
      dest_y[3] = y + height;
Packit 98cdb6
Packit 98cdb6
      if (dest_y[1] > dest_y[2])
Packit 98cdb6
	{
Packit 98cdb6
	  component_mask &= ~(COMPONENT_EAST | COMPONENT_WEST | COMPONENT_CENTER);
Packit 98cdb6
	  dest_y[1] = dest_y[2] = (dest_y[1] + dest_y[2]) / 2;
Packit 98cdb6
	}
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
#define RENDER_COMPONENT(X1,X2,Y1,Y2)					         \
Packit 98cdb6
        pixbuf_render (pixbuf, theme_pb->hints[Y1][X1], window, mask, clip_rect, \
Packit 98cdb6
	 	       src_x[X1], src_y[Y1],				         \
Packit 98cdb6
		       src_x[X2] - src_x[X1], src_y[Y2] - src_y[Y1],	         \
Packit 98cdb6
		       dest_x[X1], dest_y[Y1],				         \
Packit 98cdb6
		       dest_x[X2] - dest_x[X1], dest_y[Y2] - dest_y[Y1]);
Packit 98cdb6
      
Packit 98cdb6
      if (component_mask & COMPONENT_NORTH_WEST)
Packit 98cdb6
	RENDER_COMPONENT (0, 1, 0, 1);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_NORTH)
Packit 98cdb6
	RENDER_COMPONENT (1, 2, 0, 1);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_NORTH_EAST)
Packit 98cdb6
	RENDER_COMPONENT (2, 3, 0, 1);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_WEST)
Packit 98cdb6
	RENDER_COMPONENT (0, 1, 1, 2);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_CENTER)
Packit 98cdb6
	RENDER_COMPONENT (1, 2, 1, 2);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_EAST)
Packit 98cdb6
	RENDER_COMPONENT (2, 3, 1, 2);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_SOUTH_WEST)
Packit 98cdb6
	RENDER_COMPONENT (0, 1, 2, 3);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_SOUTH)
Packit 98cdb6
	RENDER_COMPONENT (1, 2, 2, 3);
Packit 98cdb6
Packit 98cdb6
      if (component_mask & COMPONENT_SOUTH_EAST)
Packit 98cdb6
	RENDER_COMPONENT (2, 3, 2, 3);
Packit 98cdb6
    }
Packit 98cdb6
  else
Packit 98cdb6
    {
Packit 98cdb6
      if (center)
Packit 98cdb6
	{
Packit 98cdb6
	  x += (width - pixbuf_width) / 2;
Packit 98cdb6
	  y += (height - pixbuf_height) / 2;
Packit 98cdb6
	  
Packit 98cdb6
	  pixbuf_render (pixbuf, 0, window, NULL, clip_rect,
Packit 98cdb6
			 0, 0,
Packit 98cdb6
			 pixbuf_width, pixbuf_height,
Packit 98cdb6
			 x, y,
Packit 98cdb6
			 pixbuf_width, pixbuf_height);
Packit 98cdb6
	}
Packit 98cdb6
      else
Packit 98cdb6
	{
Packit 98cdb6
          cairo_t *cr = gdk_cairo_create (window);
Packit 98cdb6
Packit 98cdb6
          gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
Packit 98cdb6
          cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
Packit 98cdb6
Packit 98cdb6
	  if (clip_rect)
Packit 98cdb6
	    gdk_cairo_rectangle (cr, clip_rect);
Packit 98cdb6
	  else
Packit 98cdb6
	    cairo_rectangle (cr, x, y, width, height);
Packit 98cdb6
	  
Packit 98cdb6
          cairo_fill (cr);
Packit 98cdb6
Packit 98cdb6
          cairo_destroy (cr);
Packit 98cdb6
	}
Packit 98cdb6
    }
Packit 98cdb6
}