text: /*
   *
   * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
   *             2005 Lars Knoll & Zack Rusin, Trolltech
   *             2008 Aaron Plattner, NVIDIA Corporation
   *
   * 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 Keith Packard not be used in
   * advertising or publicity pertaining to distribution of the software without
   * specific, written prior permission.  Keith Packard 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 <string.h>
  #include <math.h>
  #include <assert.h>
  #include <limits.h>
  
  #include "pixman-private.h"
  
  #ifdef PIXMAN_FB_ACCESSORS
  #define PIXMAN_COMPOSITE_RECT_GENERAL pixman_composite_rect_general_accessors
  #else
  #define PIXMAN_COMPOSITE_RECT_GENERAL pixman_composite_rect_general_no_accessors
  #endif
  
  static unsigned int
  SourcePictureClassify (source_image_t *pict,
  		       int	       x,
  		       int	       y,
  		       int	       width,
  		       int	       height)
  {
      if (pict->common.type == SOLID)
      {
  	pict->class = SOURCE_IMAGE_CLASS_HORIZONTAL;
      }
      else if (pict->common.type == LINEAR)
      {
  	linear_gradient_t *linear = (linear_gradient_t *)pict;
  	pixman_vector_t   v;
  	pixman_fixed_32_32_t l;
  	pixman_fixed_48_16_t dx, dy, a, b, off;
  	pixman_fixed_48_16_t factors[4];
  	int	     i;
  
  	dx = linear->p2.x - linear->p1.x;
  	dy = linear->p2.y - linear->p1.y;
  	l = dx * dx + dy * dy;
  	if (l)
  	{
  	    a = (dx << 32) / l;
  	    b = (dy << 32) / l;
  	}
  	else
  	{
  	    a = b = 0;
  	}
  
  	off = (-a * linear->p1.x
  	       -b * linear->p1.y) >> 16;
  
  	for (i = 0; i < 3; i++)
  	{
  	    v.vector[0] = pixman_int_to_fixed ((i % 2) * (width  - 1) + x);
  	    v.vector[1] = pixman_int_to_fixed ((i / 2) * (height - 1) + y);
  	    v.vector[2] = pixman_fixed_1;
  
  	    if (pict->common.transform)
  	    {
  		if (!pixman_transform_point_3d (pict->common.transform, &v))
  		    return SOURCE_IMAGE_CLASS_UNKNOWN;
  	    }
  
  	    factors[i] = ((a * v.vector[0] + b * v.vector[1]) >> 16) + off;
  	}
  
  	if (factors[2] == factors[0])
  	    pict->class = SOURCE_IMAGE_CLASS_HORIZONTAL;
  	else if (factors[1] == factors[0])
  	    pict->class = SOURCE_IMAGE_CLASS_VERTICAL;
      }
  
      return pict->class;
  }
  
  static void fbFetchSolid(bits_image_t * pict, int x, int y, int width, uint32_t *buffer, uint32_t *mask, uint32_t maskBits)
  {
      uint32_t color;
      uint32_t *end;
      fetchPixelProc32 fetch = ACCESS(pixman_fetchPixelProcForPicture32)(pict);
  
      color = fetch(pict, 0, 0);
  
      end = buffer + width;
      while (buffer < end)
  	*(buffer++) = color;
  }
  
  static void fbFetchSolid64(bits_image_t * pict, int x, int y, int width, uint64_t *buffer, void *unused, uint32_t unused2)
  {
      uint64_t color;
      uint64_t *end;
      fetchPixelProc64 fetch = ACCESS(pixman_fetchPixelProcForPicture64)(pict);
  
      color = fetch(pict, 0, 0);
  
      end = buffer + width;
      while (buffer < end)
  	*(buffer++) = color;
  }
  
  static void fbFetch(bits_image_t * pict, int x, int y, int width, uint32_t *buffer, uint32_t *mask, uint32_t maskBits)
  {
      fetchProc32 fetch = ACCESS(pixman_fetchProcForPicture32)(pict);
  
      fetch(pict, x, y, width, buffer);
  }
  
  static void fbFetch64(bits_image_t * pict, int x, int y, int width, uint64_t *buffer, void *unused, uint32_t unused2)
  {
      fetchProc64 fetch = ACCESS(pixman_fetchProcForPicture64)(pict);
  
      fetch(pict, x, y, width, buffer);
  }
  
  static void
  fbStore(bits_image_t * pict, int x, int y, int width, uint32_t *buffer)
  {
      uint32_t *bits;
      int32_t stride;
      storeProc32 store = ACCESS(pixman_storeProcForPicture32)(pict);
      const pixman_indexed_t * indexed = pict->indexed;
  
      bits = pict->bits;
      stride = pict->rowstride;
      bits += y*stride;
      store((pixman_image_t *)pict, bits, buffer, x, width, indexed);
  }
  
  static void
  fbStore64(bits_image_t * pict, int x, int y, int width, uint64_t *buffer)
  {
      uint32_t *bits;
      int32_t stride;
      storeProc64 store = ACCESS(pixman_storeProcForPicture64)(pict);
      const pixman_indexed_t * indexed = pict->indexed;
  
      bits = pict->bits;
      stride = pict->rowstride;
      bits += y*stride;
      store((pixman_image_t *)pict, bits, buffer, x, width, indexed);
  }
  
  typedef void (*scanStoreProc)(pixman_image_t *, int, int, int, uint32_t *);
  typedef void (*scanFetchProc)(pixman_image_t *, int, int, int, uint32_t *,
  			      uint32_t *, uint32_t);
  
  static inline scanFetchProc get_fetch_source_pict(const int wide)
  {
      if (wide)
  	return (scanFetchProc)pixmanFetchSourcePict64;
      else
  	return (scanFetchProc)pixmanFetchSourcePict;
  }
  
  static inline scanFetchProc get_fetch_solid(const int wide)
  {
      if (wide)
  	return (scanFetchProc)fbFetchSolid64;
      else
  	return (scanFetchProc)fbFetchSolid;
  }
  
  static inline scanFetchProc get_fetch(const int wide)
  {
      if (wide)
  	return (scanFetchProc)fbFetch64;
      else
  	return (scanFetchProc)fbFetch;
  }
  
  static inline scanFetchProc get_fetch_external_alpha(const int wide)
  {
      if (wide)
  	return (scanFetchProc)ACCESS(fbFetchExternalAlpha64);
      else
  	return (scanFetchProc)ACCESS(fbFetchExternalAlpha);
  }
  
  static inline scanFetchProc get_fetch_transformed(const int wide)
  {
      if (wide)
  	return (scanFetchProc)ACCESS(fbFetchTransformed64);
      else
  	return (scanFetchProc)ACCESS(fbFetchTransformed);
  }
  
  static inline scanStoreProc get_store(const int wide)
  {
      if (wide)
  	return (scanStoreProc)fbStore64;
      else
  	return (scanStoreProc)fbStore;
  }
  
  static inline scanStoreProc get_store_external_alpha(const int wide)
  {
      if (wide)
  	return (scanStoreProc)ACCESS(fbStoreExternalAlpha64);
      else
  	return (scanStoreProc)ACCESS(fbStoreExternalAlpha);
  }
  
  #ifndef PIXMAN_FB_ACCESSORS
  static
  #endif
  void
  PIXMAN_COMPOSITE_RECT_GENERAL (const FbComposeData *data,
  			       void *src_buffer, void *mask_buffer, 
  			       void *dest_buffer, const int wide)
  {
      int i;
      scanStoreProc store;
      scanFetchProc fetchSrc = NULL, fetchMask = NULL, fetchDest = NULL;
      unsigned int srcClass = SOURCE_IMAGE_CLASS_UNKNOWN;
      unsigned int maskClass = SOURCE_IMAGE_CLASS_UNKNOWN;
      uint32_t *bits;
      int32_t stride;
      int xoff, yoff;
  
      if (data->op == PIXMAN_OP_CLEAR)
          fetchSrc = NULL;
      else if (IS_SOURCE_IMAGE (data->src))
      {
  	fetchSrc = get_fetch_source_pict(wide);
  	srcClass = SourcePictureClassify ((source_image_t *)data->src,
  					  data->xSrc, data->ySrc,
  					  data->width, data->height);
      }
      else
      {
  	bits_image_t *bits = (bits_image_t *)data->src;
  
  	if (bits->common.alpha_map)
  	{
  	    fetchSrc = get_fetch_external_alpha(wide);
  	}
  	else if ((bits->common.repeat == PIXMAN_REPEAT_NORMAL || bits->common.repeat == PIXMAN_REPEAT_PAD) &&
  		 bits->width == 1 &&
  		 bits->height == 1)
  	{
  	    fetchSrc = get_fetch_solid(wide);
  	    srcClass = SOURCE_IMAGE_CLASS_HORIZONTAL;
  	}
  	else if (!bits->common.transform && bits->common.filter != PIXMAN_FILTER_CONVOLUTION
                  && bits->common.repeat != PIXMAN_REPEAT_PAD)
  	{
  	    fetchSrc = get_fetch(wide);
  	}
  	else
  	{
  	    fetchSrc = get_fetch_transformed(wide);
  	}
      }
  
      if (!data->mask || data->op == PIXMAN_OP_CLEAR)
      {
  	fetchMask = NULL;
      }
      else
      {
  	if (IS_SOURCE_IMAGE (data->mask))
  	{
  	    fetchMask = (scanFetchProc)pixmanFetchSourcePict;
  	    maskClass = SourcePictureClassify ((source_image_t *)data->mask,
  					       data->xMask, data->yMask,
  					       data->width, data->height);
  	}
  	else
  	{
  	    bits_image_t *bits = (bits_image_t *)data->mask;
  
  	    if (bits->common.alpha_map)
  	    {
  		fetchMask = get_fetch_external_alpha(wide);
  	    }
  	    else if ((bits->common.repeat == PIXMAN_REPEAT_NORMAL || bits->common.repeat == PIXMAN_REPEAT_PAD) &&
  		     bits->width == 1 && bits->height == 1)
  	    {
  		fetchMask = get_fetch_solid(wide);
  		maskClass = SOURCE_IMAGE_CLASS_HORIZONTAL;
  	    }
  	    else if (!bits->common.transform && bits->common.filter != PIXMAN_FILTER_CONVOLUTION
                      && bits->common.repeat != PIXMAN_REPEAT_PAD)
  		fetchMask = get_fetch(wide);
  	    else
  		fetchMask = get_fetch_transformed(wide);
  	}
      }
  
      if (data->dest->common.alpha_map)
      {
  	fetchDest = get_fetch_external_alpha(wide);
  	store = get_store_external_alpha(wide);
  
  	if (data->op == PIXMAN_OP_CLEAR || data->op == PIXMAN_OP_SRC)
  	    fetchDest = NULL;
      }
      else
      {
  	fetchDest = get_fetch(wide);
  	store = get_store(wide);
  
  	switch (data->op)
  	{
  	case PIXMAN_OP_CLEAR:
  	case PIXMAN_OP_SRC:
  	    fetchDest = NULL;
  #ifndef PIXMAN_FB_ACCESSORS
  	    /* fall-through */
  	case PIXMAN_OP_ADD:
  	case PIXMAN_OP_OVER:
  	    switch (data->dest->bits.format) {
  	    case PIXMAN_a8r8g8b8:
  	    case PIXMAN_x8r8g8b8:
  		// Skip the store step and composite directly into the
  		// destination if the output format of the compose func matches
  		// the destination format.
  		if (!wide)
  		    store = NULL;
  		break;
  	    default:
  		break;
  	    }
  #endif
  	    break;
  	}
      }
  
      if (!store)
      {
  	bits = data->dest->bits.bits;
  	stride = data->dest->bits.rowstride;
  	xoff = yoff = 0;
      }
      else
      {
  	bits = NULL;
  	stride = 0;
  	xoff = yoff = 0;
      }
  
      if (fetchSrc		   &&
  	fetchMask		   &&
  	data->mask		   &&
  	data->mask->common.type == BITS &&
  	data->mask->common.component_alpha &&
  	PIXMAN_FORMAT_RGB (data->mask->bits.format))
      {
  	CombineFuncC32 compose =
  	    wide ? (CombineFuncC32)pixman_composeFunctions64.combineC[data->op] :
  		   pixman_composeFunctions.combineC[data->op];
  	if (!compose)
  	    return;
  
  	for (i = 0; i < data->height; ++i) {
  	    /* fill first half of scanline with source */
  	    if (fetchSrc)
  	    {
  		if (fetchMask)
  		{
  		    /* fetch mask before source so that fetching of
  		       source can be optimized */
  		    fetchMask (data->mask, data->xMask, data->yMask + i,
  			       data->width, mask_buffer, 0, 0);
  
  		    if (maskClass == SOURCE_IMAGE_CLASS_HORIZONTAL)
  			fetchMask = NULL;
  		}
  
  		if (srcClass == SOURCE_IMAGE_CLASS_HORIZONTAL)
  		{
  		    fetchSrc (data->src, data->xSrc, data->ySrc + i,
  			      data->width, src_buffer, 0, 0);
  		    fetchSrc = NULL;
  		}
  		else
  		{
  		    fetchSrc (data->src, data->xSrc, data->ySrc + i,
  			      data->width, src_buffer, mask_buffer,
  			      0xffffffff);
  		}
  	    }
  	    else if (fetchMask)
  	    {
  		fetchMask (data->mask, data->xMask, data->yMask + i,
  			   data->width, mask_buffer, 0, 0);
  	    }
  
  	    if (store)
  	    {
  		/* fill dest into second half of scanline */
  		if (fetchDest)
  		    fetchDest (data->dest, data->xDest, data->yDest + i,
  			       data->width, dest_buffer, 0, 0);
  
  		/* blend */
  		compose (dest_buffer, src_buffer, mask_buffer, data->width);
  
  		/* write back */
  		store (data->dest, data->xDest, data->yDest + i, data->width,
  		       dest_buffer);
  	    }
  	    else
  	    {
  		/* blend */
  		compose (bits + (data->yDest + i+ yoff) * stride +
  			 data->xDest + xoff,
  			 src_buffer, mask_buffer, data->width);
  	    }
  	}
      }
      else
      {
  	void *src_mask_buffer = 0;
  	const int useMask = (fetchMask != NULL);
  	CombineFuncU32 compose =
  	    wide ? (CombineFuncU32)pixman_composeFunctions64.combineU[data->op] :
  		   pixman_composeFunctions.combineU[data->op];
  	if (!compose)
  	    return;
  
  	for (i = 0; i < data->height; ++i) {
  	    /* fill first half of scanline with source */
  	    if (fetchSrc)
  	    {
  		if (fetchMask)
  		{
  		    /* fetch mask before source so that fetching of
  		       source can be optimized */
  		    fetchMask (data->mask, data->xMask, data->yMask + i,
  			       data->width, mask_buffer, 0, 0);
  
  		    if (maskClass == SOURCE_IMAGE_CLASS_HORIZONTAL)
  			fetchMask = NULL;
  		}
  
  		if (srcClass == SOURCE_IMAGE_CLASS_HORIZONTAL)
  		{
  		    fetchSrc (data->src, data->xSrc, data->ySrc + i,
  			      data->width, src_buffer, 0, 0);
  
  		    if (useMask)
  		    {
  			if (wide)
  			    pixman_composeFunctions64.combineU[PIXMAN_OP_IN] (mask_buffer, src_buffer, data->width);
  			else
  			    pixman_composeFunctions.combineU[PIXMAN_OP_IN] (mask_buffer, src_buffer, data->width);
  
  			src_mask_buffer = mask_buffer;
  		    }
  		    else
  			src_mask_buffer = src_buffer;
  
  		    fetchSrc = NULL;
  		}
  		else
  		{
  		    fetchSrc (data->src, data->xSrc, data->ySrc + i,
  			      data->width, src_buffer,
  			      useMask ? mask_buffer : NULL, 0xff000000);
  
  		    if (useMask) {
  			if (wide)
  			    pixman_composeFunctions64.combineMaskU (src_buffer,
  								    mask_buffer,
  								    data->width);
  			else
  			    pixman_composeFunctions.combineMaskU (src_buffer,
  								  mask_buffer,
  								  data->width);
  		    }
  
  		    src_mask_buffer = src_buffer;
  		}
  	    }
  	    else if (fetchMask)
  	    {
  		fetchMask (data->mask, data->xMask, data->yMask + i,
  			   data->width, mask_buffer, 0, 0);
  
  		if (wide)
  		    pixman_composeFunctions64.combineU[PIXMAN_OP_IN] (mask_buffer, src_buffer, data->width);
  		else
  		    pixman_composeFunctions.combineU[PIXMAN_OP_IN] (mask_buffer, src_buffer, data->width);
  
  		src_mask_buffer = mask_buffer;
  	    }
  
  	    if (store)
  	    {
  		/* fill dest into second half of scanline */
  		if (fetchDest)
  		    fetchDest (data->dest, data->xDest, data->yDest + i,
  			       data->width, dest_buffer, 0, 0);
  
  		/* blend */
  		compose (dest_buffer, src_mask_buffer, data->width);
  
  		/* write back */
  		store (data->dest, data->xDest, data->yDest + i, data->width,
  		       dest_buffer);
  	    }
  	    else
  	    {
  		/* blend */
  		compose (bits + (data->yDest + i+ yoff) * stride +
  			 data->xDest + xoff,
  			 src_mask_buffer, data->width);
  	    }
  	}
      }
  }
  
  #ifndef PIXMAN_FB_ACCESSORS
  
  #define SCANLINE_BUFFER_LENGTH 8192
  
  void
  pixman_composite_rect_general (const FbComposeData *data)
  {
      uint8_t stack_scanline_buffer[SCANLINE_BUFFER_LENGTH * 3];
      const pixman_format_code_t srcFormat = data->src->type == BITS ? data->src->bits.format : 0;
      const pixman_format_code_t maskFormat = data->mask && data->mask->type == BITS ? data->mask->bits.format : 0;
      const pixman_format_code_t destFormat = data->dest->type == BITS ? data->dest->bits.format : 0;
      const int srcWide = PIXMAN_FORMAT_16BPC(srcFormat);
      const int maskWide = data->mask && PIXMAN_FORMAT_16BPC(maskFormat);
      const int destWide = PIXMAN_FORMAT_16BPC(destFormat);
      const int wide = srcWide || maskWide || destWide;
      const int Bpp = wide ? 8 : 4;
      uint8_t *scanline_buffer = stack_scanline_buffer;
      uint8_t *src_buffer, *mask_buffer, *dest_buffer;
  
      if (data->width * Bpp > SCANLINE_BUFFER_LENGTH)
      {
  	scanline_buffer = pixman_malloc_abc (data->width, 3, Bpp);
  
  	if (!scanline_buffer)
  	    return;
      }
  
      src_buffer = scanline_buffer;
      mask_buffer = src_buffer + data->width * Bpp;
      dest_buffer = mask_buffer + data->width * Bpp;
  
      if (data->src->common.read_func			||
  	data->src->common.write_func			||
  	(data->mask && data->mask->common.read_func)	||
  	(data->mask && data->mask->common.write_func)	||
  	data->dest->common.read_func			||
  	data->dest->common.write_func)
      {
  	pixman_composite_rect_general_accessors (data, src_buffer, mask_buffer,
  						 dest_buffer, wide);
      }
      else
      {
  	pixman_composite_rect_general_no_accessors (data, src_buffer,
  						    mask_buffer, dest_buffer,
  						    wide);
      }
  
      if (scanline_buffer != stack_scanline_buffer)
  	free (scanline_buffer);
  }
  
  #endif
  