| /* |
| * |
| * 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 2048 |
| |
| void |
| pixman_composite_rect_general (const FbComposeData *data) |
| { |
| uint32_t _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 = (uint8_t*)_scanline_buffer; |
| uint8_t *src_buffer, *mask_buffer, *dest_buffer; |
| |
| if (data->width * Bpp > SCANLINE_BUFFER_LENGTH * sizeof(uint32_t)) |
| { |
| 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 ((void*)scanline_buffer != (void*)_scanline_buffer) |
| free (scanline_buffer); |
| } |
| |
| #endif |