text: /*
   * Copyright © 2009 Red Hat, 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 Red Hat not be used in advertising or
   * publicity pertaining to distribution of the software without specific,
   * written prior permission.  Red Hat makes no representations about the
   * suitability of this software for any purpose.  It is provided "as is"
   * without express or implied warranty.
   *
   * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
   * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
   * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS 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.
   */
  
  #ifdef HAVE_CONFIG_H
  #include <config.h>
  #endif
  #include <stdlib.h>
  #include "pixman-private.h"
  
  pixman_implementation_t *
  _pixman_implementation_create (pixman_implementation_t *fallback,
  			       const pixman_fast_path_t *fast_paths)
  {
      pixman_implementation_t *imp;
  
      assert (fast_paths);
  
      if ((imp = malloc (sizeof (pixman_implementation_t))))
      {
  	pixman_implementation_t *d;
  
  	memset (imp, 0, sizeof *imp);
  
  	imp->fallback = fallback;
  	imp->fast_paths = fast_paths;
  	
  	/* Make sure the whole fallback chain has the right toplevel */
  	for (d = imp; d != NULL; d = d->fallback)
  	    d->toplevel = imp;
      }
  
      return imp;
  }
  
  #define N_CACHED_FAST_PATHS 8
  
  typedef struct
  {
      struct
      {
  	pixman_implementation_t *	imp;
  	pixman_fast_path_t		fast_path;
      } cache [N_CACHED_FAST_PATHS];
  } cache_t;
  
  PIXMAN_DEFINE_THREAD_LOCAL (cache_t, fast_path_cache);
  
  static void
  dummy_composite_rect (pixman_implementation_t *imp,
  		      pixman_composite_info_t *info)
  {
  }
  
  void
  _pixman_implementation_lookup_composite (pixman_implementation_t  *toplevel,
  					 pixman_op_t               op,
  					 pixman_format_code_t      src_format,
  					 uint32_t                  src_flags,
  					 pixman_format_code_t      mask_format,
  					 uint32_t                  mask_flags,
  					 pixman_format_code_t      dest_format,
  					 uint32_t                  dest_flags,
  					 pixman_implementation_t **out_imp,
  					 pixman_composite_func_t  *out_func)
  {
      pixman_implementation_t *imp;
      cache_t *cache;
      int i;
  
      /* Check cache for fast paths */
      cache = PIXMAN_GET_THREAD_LOCAL (fast_path_cache);
  
      for (i = 0; i < N_CACHED_FAST_PATHS; ++i)
      {
  	const pixman_fast_path_t *info = &(cache->cache[i].fast_path);
  
  	/* Note that we check for equality here, not whether
  	 * the cached fast path matches. This is to prevent
  	 * us from selecting an overly general fast path
  	 * when a more specific one would work.
  	 */
  	if (info->op == op			&&
  	    info->src_format == src_format	&&
  	    info->mask_format == mask_format	&&
  	    info->dest_format == dest_format	&&
  	    info->src_flags == src_flags	&&
  	    info->mask_flags == mask_flags	&&
  	    info->dest_flags == dest_flags	&&
  	    info->func)
  	{
  	    *out_imp = cache->cache[i].imp;
  	    *out_func = cache->cache[i].fast_path.func;
  
  	    goto update_cache;
  	}
      }
  
      for (imp = toplevel; imp != NULL; imp = imp->fallback)
      {
  	const pixman_fast_path_t *info = imp->fast_paths;
  
  	while (info->op != PIXMAN_OP_NONE)
  	{
  	    if ((info->op == op || info->op == PIXMAN_OP_any)		&&
  		/* Formats */
  		((info->src_format == src_format) ||
  		 (info->src_format == PIXMAN_any))			&&
  		((info->mask_format == mask_format) ||
  		 (info->mask_format == PIXMAN_any))			&&
  		((info->dest_format == dest_format) ||
  		 (info->dest_format == PIXMAN_any))			&&
  		/* Flags */
  		(info->src_flags & src_flags) == info->src_flags	&&
  		(info->mask_flags & mask_flags) == info->mask_flags	&&
  		(info->dest_flags & dest_flags) == info->dest_flags)
  	    {
  		*out_imp = imp;
  		*out_func = info->func;
  
  		/* Set i to the last spot in the cache so that the
  		 * move-to-front code below will work
  		 */
  		i = N_CACHED_FAST_PATHS - 1;
  
  		goto update_cache;
  	    }
  
  	    ++info;
  	}
      }
  
      /* We should never reach this point */
      _pixman_log_error (
          FUNC,
          "No composite function found\n"
          "\n"
          "The most likely cause of this is that this system has issues with\n"
          "thread local storage\n");
  
      *out_imp = NULL;
      *out_func = dummy_composite_rect;
      return;
  
  update_cache:
      if (i)
      {
  	while (i--)
  	    cache->cache[i + 1] = cache->cache[i];
  
  	cache->cache[0].imp = *out_imp;
  	cache->cache[0].fast_path.op = op;
  	cache->cache[0].fast_path.src_format = src_format;
  	cache->cache[0].fast_path.src_flags = src_flags;
  	cache->cache[0].fast_path.mask_format = mask_format;
  	cache->cache[0].fast_path.mask_flags = mask_flags;
  	cache->cache[0].fast_path.dest_format = dest_format;
  	cache->cache[0].fast_path.dest_flags = dest_flags;
  	cache->cache[0].fast_path.func = *out_func;
      }
  }
  
  static void
  dummy_combine (pixman_implementation_t *imp,
  	       pixman_op_t              op,
  	       uint32_t *               pd,
  	       const uint32_t *         ps,
  	       const uint32_t *         pm,
  	       int                      w)
  {
  }
  
  pixman_combine_32_func_t
  _pixman_implementation_lookup_combiner (pixman_implementation_t *imp,
  					pixman_op_t		 op,
  					pixman_bool_t		 component_alpha,
  					pixman_bool_t		 narrow)
  {
      while (imp)
      {
  	pixman_combine_32_func_t f = NULL;
  
  	switch ((narrow << 1) | component_alpha)
  	{
  	case 0: /* not narrow, not component alpha */
  	    f = (pixman_combine_32_func_t)imp->combine_float[op];
  	    break;
  	    
  	case 1: /* not narrow, component_alpha */
  	    f = (pixman_combine_32_func_t)imp->combine_float_ca[op];
  	    break;
  
  	case 2: /* narrow, not component alpha */
  	    f = imp->combine_32[op];
  	    break;
  
  	case 3: /* narrow, component_alpha */
  	    f = imp->combine_32_ca[op];
  	    break;
  	}
  
  	if (f)
  	    return f;
  
  	imp = imp->fallback;
      }
  
      /* We should never reach this point */
      _pixman_log_error (FUNC, "No known combine function\n");
      return dummy_combine;
  }
  
  pixman_bool_t
  _pixman_implementation_blt (pixman_implementation_t * imp,
                              uint32_t *                src_bits,
                              uint32_t *                dst_bits,
                              int                       src_stride,
                              int                       dst_stride,
                              int                       src_bpp,
                              int                       dst_bpp,
                              int                       src_x,
                              int                       src_y,
                              int                       dest_x,
                              int                       dest_y,
                              int                       width,
                              int                       height)
  {
      while (imp)
      {
  	if (imp->blt &&
  	    (*imp->blt) (imp, src_bits, dst_bits, src_stride, dst_stride,
  			 src_bpp, dst_bpp, src_x, src_y, dest_x, dest_y,
  			 width, height))
  	{
  	    return TRUE;
  	}
  
  	imp = imp->fallback;
      }
  
      return FALSE;
  }
  
  pixman_bool_t
  _pixman_implementation_fill (pixman_implementation_t *imp,
                               uint32_t *               bits,
                               int                      stride,
                               int                      bpp,
                               int                      x,
                               int                      y,
                               int                      width,
                               int                      height,
                               uint32_t                 filler)
  {
      while (imp)
      {
  	if (imp->fill &&
  	    ((*imp->fill) (imp, bits, stride, bpp, x, y, width, height, filler)))
  	{
  	    return TRUE;
  	}
  
  	imp = imp->fallback;
      }
  
      return FALSE;
  }
  
  static uint32_t *
  get_scanline_null (pixman_iter_t *iter, const uint32_t *mask)
  {
      return NULL;
  }
  
  void
  _pixman_implementation_iter_init (pixman_implementation_t *imp,
                                    pixman_iter_t           *iter,
                                    pixman_image_t          *image,
                                    int                      x,
                                    int                      y,
                                    int                      width,
                                    int                      height,
                                    uint8_t                 *buffer,
                                    iter_flags_t             iter_flags,
                                    uint32_t                 image_flags)
  {
      pixman_format_code_t format;
  
      iter->image = image;
      iter->buffer = (uint32_t *)buffer;
      iter->x = x;
      iter->y = y;
      iter->width = width;
      iter->height = height;
      iter->iter_flags = iter_flags;
      iter->image_flags = image_flags;
      iter->fini = NULL;
  
      if (!iter->image)
      {
  	iter->get_scanline = get_scanline_null;
  	return;
      }
  
      format = iter->image->common.extended_format_code;
  
      while (imp)
      {
          if (imp->iter_info)
          {
              const pixman_iter_info_t *info;
  
              for (info = imp->iter_info; info->format != PIXMAN_null; ++info)
              {
                  if ((info->format == PIXMAN_any || info->format == format) &&
                      (info->image_flags & image_flags) == info->image_flags &&
                      (info->iter_flags & iter_flags) == info->iter_flags)
                  {
                      iter->get_scanline = info->get_scanline;
                      iter->write_back = info->write_back;
  
                      if (info->initializer)
                          info->initializer (iter, info);
                      return;
                  }
              }
          }
  
          imp = imp->fallback;
      }
  }
  
  pixman_bool_t
  _pixman_disabled (const char *name)
  {
      const char *env;
  
      if ((env = getenv ("PIXMAN_DISABLE")))
      {
  	do
  	{
  	    const char *end;
  	    int len;
  
  	    if ((end = strchr (env, ' ')))
  		len = end - env;
  	    else
  		len = strlen (env);
  
  	    if (strlen (name) == len && strncmp (name, env, len) == 0)
  	    {
  		printf ("pixman: Disabled %s implementation\n", name);
  		return TRUE;
  	    }
  
  	    env += len;
  	}
  	while (*env++);
      }
  
      return FALSE;
  }
  
  static const pixman_fast_path_t empty_fast_path[] =
  {
      { PIXMAN_OP_NONE }
  };
  
  static pixman_implementation_t * __pixman_default_imp(pixman_implementation_t * imp)
  {
      return imp;
  }
  extern __typeof(__pixman_default_imp) _pixman_x64_get_implementations __attribute__((weak, alias("__pixman_default_imp")));
  extern __typeof(__pixman_default_imp) _pixman_arm32_get_implementations __attribute__((weak, alias("__pixman_default_imp")));
  extern __typeof(__pixman_default_imp) _pixman_arm64_get_implementations __attribute__((weak, alias("__pixman_default_imp")));
  
  pixman_implementation_t *
  _pixman_choose_implementation (void)
  {
      pixman_implementation_t *imp;
  
      imp = _pixman_implementation_create_general();
  
      if (!_pixman_disabled ("fast"))
  	imp = _pixman_implementation_create_fast_path (imp);
  
  	imp = _pixman_x64_get_implementations (imp);
  	imp = _pixman_arm32_get_implementations (imp);
  	imp = _pixman_arm64_get_implementations (imp);
  
  	imp = _pixman_implementation_create_noop (imp);
  
      if (_pixman_disabled ("wholeops"))
      {
          pixman_implementation_t *cur;
  
          /* Disable all whole-operation paths except the general one,
           * so that optimized iterators are used as much as possible.
           */
          for (cur = imp; cur->fallback; cur = cur->fallback)
              cur->fast_paths = empty_fast_path;
      }
  
      return imp;
  }
  