/* * * 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 #endif #include #include #include #include #include #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