| /* Copyright © 2007 Bart Massey |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * Except as contained in this notice, the names of the authors or their |
| * institutions shall not be used in advertising or otherwise to promote the |
| * sale, use or other dealings in this Software without prior written |
| * authorization from the authors. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <xcb/xcb.h> |
| #include <xcb/shm.h> |
| #include <xcb/xcb_aux.h> |
| #include "xcb_bitops.h" |
| #include "xcb_image.h" |
| #define BUILD |
| #include "xcb_pixel.h" |
| |
| |
| static xcb_format_t * |
| find_format_by_depth (const xcb_setup_t *setup, uint8_t depth) |
| { |
| xcb_format_t *fmt = xcb_setup_pixmap_formats(setup); |
| xcb_format_t *fmtend = fmt + xcb_setup_pixmap_formats_length(setup); |
| for(; fmt != fmtend; ++fmt) |
| if(fmt->depth == depth) |
| return fmt; |
| return 0; |
| } |
| |
| |
| static xcb_image_format_t |
| effective_format(xcb_image_format_t format, uint8_t bpp) |
| { |
| if (format == XCB_IMAGE_FORMAT_Z_PIXMAP && bpp != 1) |
| return format; |
| return XCB_IMAGE_FORMAT_XY_PIXMAP; |
| } |
| |
| |
| static int |
| format_valid (uint8_t depth, uint8_t bpp, uint8_t unit, |
| xcb_image_format_t format, uint8_t xpad) |
| { |
| xcb_image_format_t ef = effective_format(format, bpp); |
| if (depth > bpp) |
| return 0; |
| switch(ef) { |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| switch(unit) { |
| case 8: |
| case 16: |
| case 32: |
| break; |
| default: |
| return 0; |
| } |
| if (xpad < bpp) |
| return 0; |
| switch (xpad) { |
| case 8: |
| case 16: |
| case 32: |
| break; |
| default: |
| return 0; |
| } |
| break; |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| switch (bpp) { |
| case 4: |
| if (unit != 8) |
| return 0; |
| break; |
| case 8: |
| case 16: |
| case 24: |
| case 32: |
| if (unit != bpp) |
| return 0; |
| break; |
| default: |
| return 0; |
| } |
| break; |
| default: |
| return 0; |
| } |
| return 1; |
| } |
| |
| |
| static int |
| image_format_valid (xcb_image_t *image) { |
| return format_valid(image->depth, |
| image->bpp, |
| image->unit, |
| image->format, |
| image->scanline_pad); |
| } |
| |
| |
| void |
| xcb_image_annotate (xcb_image_t *image) |
| { |
| xcb_image_format_t ef = effective_format(image->format, image->bpp); |
| switch (ef) { |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| image->stride = xcb_roundup(image->width, image->scanline_pad) >> 3; |
| image->size = image->height * image->stride * image->depth; |
| break; |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| image->stride = xcb_roundup((uint32_t)image->width * |
| (uint32_t)image->bpp, |
| image->scanline_pad) >> 3; |
| image->size = image->height * image->stride; |
| break; |
| default: |
| assert(0); |
| } |
| } |
| |
| |
| xcb_image_t * |
| xcb_image_create_native (xcb_connection_t * c, |
| uint16_t width, |
| uint16_t height, |
| xcb_image_format_t format, |
| uint8_t depth, |
| void * base, |
| uint32_t bytes, |
| uint8_t * data) |
| { |
| const xcb_setup_t * setup = xcb_get_setup(c); |
| xcb_format_t * fmt; |
| xcb_image_format_t ef = format; |
| |
| if (ef == XCB_IMAGE_FORMAT_Z_PIXMAP && depth == 1) |
| ef = XCB_IMAGE_FORMAT_XY_PIXMAP; |
| switch (ef) { |
| case XCB_IMAGE_FORMAT_XY_BITMAP: |
| if (depth != 1) |
| return 0; |
| /* fall through */ |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| if (depth > 1) { |
| fmt = find_format_by_depth(setup, depth); |
| if (!fmt) |
| return 0; |
| } |
| return xcb_image_create(width, height, format, |
| setup->bitmap_format_scanline_pad, |
| depth, depth, setup->bitmap_format_scanline_unit, |
| setup->image_byte_order, |
| setup->bitmap_format_bit_order, |
| base, bytes, data); |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| fmt = find_format_by_depth(setup, depth); |
| if (!fmt) |
| return 0; |
| return xcb_image_create(width, height, format, |
| fmt->scanline_pad, |
| fmt->depth, fmt->bits_per_pixel, 0, |
| setup->image_byte_order, |
| XCB_IMAGE_ORDER_MSB_FIRST, |
| base, bytes, data); |
| default: |
| assert(0); |
| } |
| assert(0); |
| } |
| |
| |
| xcb_image_t * |
| xcb_image_create (uint16_t width, |
| uint16_t height, |
| xcb_image_format_t format, |
| uint8_t xpad, |
| uint8_t depth, |
| uint8_t bpp, |
| uint8_t unit, |
| xcb_image_order_t byte_order, |
| xcb_image_order_t bit_order, |
| void * base, |
| uint32_t bytes, |
| uint8_t * data) |
| { |
| xcb_image_t * image; |
| |
| if (unit == 0) { |
| switch (format) { |
| case XCB_IMAGE_FORMAT_XY_BITMAP: |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| unit = 32; |
| break; |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| if (bpp == 1) { |
| unit = 32; |
| break; |
| } |
| if (bpp < 8) { |
| unit = 8; |
| break; |
| } |
| unit = bpp; |
| break; |
| } |
| } |
| if (!format_valid(depth, bpp, unit, format, xpad)) |
| return 0; |
| image = malloc(sizeof(*image)); |
| if (image == 0) |
| return 0; |
| image->width = width; |
| image->height = height; |
| image->format = format; |
| image->scanline_pad = xpad; |
| image->depth = depth; |
| image->bpp = bpp; |
| image->unit = unit; |
| image->plane_mask = xcb_mask(depth); |
| image->byte_order = byte_order; |
| image->bit_order = bit_order; |
| xcb_image_annotate(image); |
| |
| /* |
| * Ways this function can be called: |
| * * with data: we fail if bytes isn't |
| * large enough, else leave well enough alone. |
| * * with base and !data: if bytes is zero, we |
| * default; otherwise we fail if bytes isn't |
| * large enough, else fill in data |
| * * with !base and !data: we malloc storage |
| * for the data, save that address as the base, |
| * and fail if malloc does. |
| * |
| * When successful, we establish the invariant that data |
| * points at sufficient storage that may have been |
| * supplied, and base is set iff it should be |
| * auto-freed when the image is destroyed. |
| * |
| * Except as a special case when base = 0 && data == 0 && |
| * bytes == ~0 we just return the image structure and let |
| * the caller deal with getting the allocation right. |
| */ |
| if (!base && !data && bytes == ~0) { |
| image->base = 0; |
| image->data = 0; |
| return image; |
| } |
| if (!base && data && bytes == 0) |
| bytes = image->size; |
| image->base = base; |
| image->data = data; |
| if (!image->data) { |
| if (image->base) { |
| image->data = image->base; |
| } else { |
| bytes = image->size; |
| image->base = malloc(bytes); |
| image->data = image->base; |
| } |
| } |
| if (!image->data || bytes < image->size) { |
| free(image); |
| return 0; |
| } |
| return image; |
| } |
| |
| |
| void |
| xcb_image_destroy (xcb_image_t *image) |
| { |
| if (image->base) |
| free (image->base); |
| free (image); |
| } |
| |
| |
| xcb_image_t * |
| xcb_image_get (xcb_connection_t * conn, |
| xcb_drawable_t draw, |
| int16_t x, |
| int16_t y, |
| uint16_t width, |
| uint16_t height, |
| uint32_t plane_mask, |
| xcb_image_format_t format) |
| { |
| xcb_get_image_cookie_t image_cookie; |
| xcb_get_image_reply_t * imrep; |
| xcb_image_t * image = 0; |
| uint32_t bytes; |
| uint8_t * data; |
| |
| image_cookie = xcb_get_image(conn, format, draw, x, y, |
| width, height, plane_mask); |
| imrep = xcb_get_image_reply(conn, image_cookie, 0); |
| if (!imrep) |
| return 0; |
| bytes = xcb_get_image_data_length(imrep); |
| data = xcb_get_image_data(imrep); |
| switch (format) { |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| plane_mask &= xcb_mask(imrep->depth); |
| if (plane_mask != xcb_mask(imrep->depth)) { |
| xcb_image_t * tmp_image = |
| xcb_image_create_native(conn, width, height, format, |
| imrep->depth, 0, 0, 0); |
| |
| if (!tmp_image) { |
| free(imrep); |
| return 0; |
| } |
| |
| int i; |
| uint32_t rpm = plane_mask; |
| uint8_t * src_plane = image->data; |
| uint8_t * dst_plane = tmp_image->data; |
| uint32_t size = image->height * image->stride; |
| |
| if (tmp_image->bit_order == XCB_IMAGE_ORDER_MSB_FIRST) |
| rpm = xcb_bit_reverse(plane_mask, imrep->depth); |
| for (i = 0; i < imrep->depth; i++) { |
| if (rpm & 1) { |
| memcpy(dst_plane, src_plane, size); |
| src_plane += size; |
| } else { |
| memset(dst_plane, 0, size); |
| } |
| dst_plane += size; |
| } |
| tmp_image->plane_mask = plane_mask; |
| image = tmp_image; |
| free(imrep); |
| break; |
| } |
| /* fall through */ |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| image = xcb_image_create_native(conn, width, height, format, |
| imrep->depth, imrep, bytes, data); |
| if (!image) { |
| free(imrep); |
| return 0; |
| } |
| break; |
| default: |
| assert(0); |
| } |
| assert(bytes == image->size); |
| return image; |
| } |
| |
| |
| xcb_image_t * |
| xcb_image_native (xcb_connection_t * c, |
| xcb_image_t * image, |
| int convert) |
| { |
| xcb_image_t * tmp_image = 0; |
| const xcb_setup_t * setup = xcb_get_setup(c); |
| xcb_format_t * fmt = 0; |
| xcb_image_format_t ef = effective_format(image->format, image->bpp); |
| uint8_t bpp = 1; |
| |
| if (image->depth > 1 || ef == XCB_IMAGE_FORMAT_Z_PIXMAP) { |
| fmt = find_format_by_depth(setup, image->depth); |
| /* XXX For now, we don't do depth conversions, even |
| for xy-pixmaps */ |
| if (!fmt) |
| return 0; |
| bpp = fmt->bits_per_pixel; |
| } |
| switch (ef) { |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| if (setup->bitmap_format_scanline_unit != image->unit || |
| setup->bitmap_format_scanline_pad != image->scanline_pad || |
| setup->image_byte_order != image->byte_order || |
| setup->bitmap_format_bit_order != image->bit_order || |
| bpp != image->bpp) { |
| if (!convert) |
| return 0; |
| tmp_image = |
| xcb_image_create(image->width, image->height, image->format, |
| setup->bitmap_format_scanline_pad, |
| image->depth, bpp, |
| setup->bitmap_format_scanline_unit, |
| setup->image_byte_order, |
| setup->bitmap_format_bit_order, |
| 0, 0, 0); |
| if (!tmp_image) |
| return 0; |
| } |
| break; |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| if (fmt->scanline_pad != image->scanline_pad || |
| setup->image_byte_order != image->byte_order || |
| bpp != image->bpp) { |
| if (!convert) |
| return 0; |
| tmp_image = |
| xcb_image_create(image->width, image->height, image->format, |
| fmt->scanline_pad, |
| image->depth, bpp, 0, |
| setup->image_byte_order, |
| XCB_IMAGE_ORDER_MSB_FIRST, |
| 0, 0, 0); |
| if (!tmp_image) |
| return 0; |
| } |
| break; |
| default: |
| assert(0); |
| } |
| if (tmp_image) { |
| if (!xcb_image_convert(image, tmp_image)) { |
| xcb_image_destroy(tmp_image); |
| return 0; |
| } |
| image = tmp_image; |
| } |
| return image; |
| } |
| |
| |
| xcb_void_cookie_t |
| xcb_image_put (xcb_connection_t * conn, |
| xcb_drawable_t draw, |
| xcb_gcontext_t gc, |
| xcb_image_t * image, |
| int16_t x, |
| int16_t y, |
| uint8_t left_pad) |
| { |
| return xcb_put_image(conn, image->format, draw, gc, |
| image->width, image->height, |
| x, y, left_pad, |
| image->depth, |
| image->size, |
| image->data); |
| } |
| |
| |
| |
| /* |
| * Shm stuff |
| */ |
| |
| xcb_image_t * |
| xcb_image_shm_put (xcb_connection_t * conn, |
| xcb_drawable_t draw, |
| xcb_gcontext_t gc, |
| xcb_image_t * image, |
| xcb_shm_segment_info_t shminfo, |
| int16_t src_x, |
| int16_t src_y, |
| int16_t dest_x, |
| int16_t dest_y, |
| uint16_t src_width, |
| uint16_t src_height, |
| uint8_t send_event) |
| { |
| if (!xcb_image_native(conn, image, 0)) |
| return 0; |
| if (!shminfo.shmaddr) |
| return 0; |
| xcb_shm_put_image(conn, draw, gc, |
| image->width, image->height, |
| src_x, src_y, src_width, src_height, |
| dest_x, dest_y, |
| image->depth, image->format, |
| send_event, |
| shminfo.shmseg, |
| image->data - shminfo.shmaddr); |
| return image; |
| } |
| |
| |
| int |
| xcb_image_shm_get (xcb_connection_t * conn, |
| xcb_drawable_t draw, |
| xcb_image_t * image, |
| xcb_shm_segment_info_t shminfo, |
| int16_t x, |
| int16_t y, |
| uint32_t plane_mask) |
| { |
| xcb_shm_get_image_reply_t * setup; |
| xcb_shm_get_image_cookie_t cookie; |
| xcb_generic_error_t * err = 0; |
| |
| if (!shminfo.shmaddr) |
| return 0; |
| cookie = xcb_shm_get_image(conn, draw, |
| x, y, |
| image->width, image->height, |
| plane_mask, |
| image->format, |
| shminfo.shmseg, |
| image->data - shminfo.shmaddr); |
| setup = xcb_shm_get_image_reply(conn, cookie, &err); |
| if (err) { |
| fprintf(stderr, "ShmGetImageReply error %d\n", (int)err->error_code); |
| free(err); |
| return 0; |
| } else { |
| free (setup); |
| return 1; |
| } |
| } |
| |
| |
| static uint32_t |
| xy_image_byte (xcb_image_t *image, uint32_t x) |
| { |
| x >>= 3; |
| if (image->byte_order == image->bit_order) |
| return x; |
| switch (image->unit) { |
| default: |
| case 8: |
| return x; |
| case 16: |
| return x ^ 1; |
| case 32: |
| return x ^ 3; |
| } |
| } |
| |
| static uint32_t |
| xy_image_bit (xcb_image_t *image, uint32_t x) |
| { |
| x &= 7; |
| if (image->bit_order == XCB_IMAGE_ORDER_MSB_FIRST) |
| x = 7 - x; |
| return x; |
| } |
| |
| /* GetPixel/PutPixel */ |
| |
| /* XXX this is the most hideously done cut-and-paste |
| to below. Any bugs fixed there should be fixed here |
| and vice versa. */ |
| void |
| xcb_image_put_pixel (xcb_image_t *image, |
| uint32_t x, |
| uint32_t y, |
| uint32_t pixel) |
| { |
| uint8_t *row; |
| |
| if (x > image->width || y > image->height) |
| return; |
| row = image->data + (y * image->stride); |
| switch (effective_format(image->format, image->bpp)) { |
| case XCB_IMAGE_FORMAT_XY_BITMAP: |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| /* block */ { |
| int p; |
| uint32_t plane_mask = image->plane_mask; |
| uint8_t * plane = row; |
| uint32_t byte = xy_image_byte(image, x); |
| uint32_t bit = xy_image_bit(image,x); |
| uint8_t mask = 1 << bit; |
| |
| for (p = image->bpp - 1; p >= 0; p--) { |
| if ((plane_mask >> p) & 1) { |
| uint8_t * bp = plane + byte; |
| uint8_t this_bit = ((pixel >> p) & 1) << bit; |
| *bp = (*bp & ~mask) | this_bit; |
| } |
| plane += image->stride * image->height; |
| } |
| } |
| break; |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| switch (image->bpp) { |
| uint32_t mask; |
| case 4: |
| mask = 0xf; |
| pixel &= 0xf; |
| if ((x & 1) == |
| (image->byte_order == XCB_IMAGE_ORDER_MSB_FIRST)) { |
| pixel <<= 4; |
| mask <<= 4; |
| } |
| row[x >> 1] = (row[x >> 1] & ~mask) | pixel; |
| break; |
| case 8: |
| row[x] = pixel; |
| break; |
| case 16: |
| switch (image->byte_order) { |
| case XCB_IMAGE_ORDER_LSB_FIRST: |
| row[x << 1] = pixel; |
| row[(x << 1) + 1] = pixel >> 8; |
| break; |
| case XCB_IMAGE_ORDER_MSB_FIRST: |
| row[x << 1] = pixel >> 8; |
| row[(x << 1) + 1] = pixel; |
| break; |
| } |
| break; |
| case 24: |
| switch (image->byte_order) { |
| case XCB_IMAGE_ORDER_LSB_FIRST: |
| row[x * 3] = pixel; |
| row[x * 3 + 1] = pixel >> 8; |
| row[x * 3 + 2] = pixel >> 16; |
| break; |
| case XCB_IMAGE_ORDER_MSB_FIRST: |
| row[x * 3] = pixel >> 16; |
| row[x * 3 + 1] = pixel >> 8; |
| row[x * 3 + 2] = pixel; |
| break; |
| } |
| break; |
| case 32: |
| switch (image->byte_order) { |
| case XCB_IMAGE_ORDER_LSB_FIRST: |
| row[x << 2] = pixel; |
| row[(x << 2) + 1] = pixel >> 8; |
| row[(x << 2) + 2] = pixel >> 16; |
| row[(x << 2) + 3] = pixel >> 24; |
| break; |
| case XCB_IMAGE_ORDER_MSB_FIRST: |
| row[x << 2] = pixel >> 24; |
| row[(x << 2) + 1] = pixel >> 16; |
| row[(x << 2) + 2] = pixel >> 8; |
| row[(x << 2) + 3] = pixel; |
| break; |
| } |
| break; |
| default: |
| assert(0); |
| } |
| break; |
| default: |
| assert(0); |
| } |
| } |
| |
| |
| /* XXX this is the most hideously done cut-and-paste |
| from above. Any bugs fixed there should be fixed here |
| and vice versa. */ |
| uint32_t |
| xcb_image_get_pixel (xcb_image_t *image, |
| uint32_t x, |
| uint32_t y) |
| { |
| uint32_t pixel = 0; |
| uint8_t *row; |
| |
| assert(x < image->width && y < image->height); |
| row = image->data + (y * image->stride); |
| switch (effective_format(image->format, image->bpp)) { |
| case XCB_IMAGE_FORMAT_XY_BITMAP: |
| case XCB_IMAGE_FORMAT_XY_PIXMAP: |
| /* block */ { |
| int p; |
| uint32_t plane_mask = image->plane_mask; |
| uint8_t * plane = row; |
| uint32_t byte = xy_image_byte(image, x); |
| uint32_t bit = xy_image_bit(image,x); |
| |
| for (p = image->bpp - 1; p >= 0; p--) { |
| pixel <<= 1; |
| if ((plane_mask >> p) & 1) { |
| uint8_t * bp = plane + byte; |
| pixel |= (*bp >> bit) & 1; |
| } |
| plane += image->stride * image->height; |
| } |
| } |
| return pixel; |
| case XCB_IMAGE_FORMAT_Z_PIXMAP: |
| switch (image->bpp) { |
| case 4: |
| if ((x & 1) == (image->byte_order == XCB_IMAGE_ORDER_MSB_FIRST)) |
| return row[x >> 1] >> 4; |
| return row[x >> 1] & 0xf; |
| case 8: |
| return row[x]; |
| case 16: |
| switch (image->byte_order) { |
| case XCB_IMAGE_ORDER_LSB_FIRST: |
| pixel = row[x << 1]; |
| pixel |= row[(x << 1) + 1] << 8; |
| break; |
| case XCB_IMAGE_ORDER_MSB_FIRST: |
| pixel = row[x << 1] << 8; |
| pixel |= row[(x << 1) + 1]; |
| break; |
| } |
| break; |
| case 24: |
| switch (image->byte_order) { |
| case XCB_IMAGE_ORDER_LSB_FIRST: |
| pixel = row[x * 3]; |
| pixel |= row[x * 3 + 1] << 8; |
| pixel |= row[x * 3 + 2] << 16; |
| break; |
| case XCB_IMAGE_ORDER_MSB_FIRST: |
| pixel = row[x * 3] << 16; |
| pixel |= row[x * 3 + 1] << 8; |
| pixel |= row[x * 3 + 2]; |
| break; |
| } |
| break; |
| case 32: |
| switch (image->byte_order) { |
| case XCB_IMAGE_ORDER_LSB_FIRST: |
| pixel = row[x << 2]; |
| pixel |= row[(x << 2) + 1] << 8; |
| pixel |= row[(x << 2) + 2] << 16; |
| pixel |= row[(x << 2) + 3] << 24; |
| break; |
| case XCB_IMAGE_ORDER_MSB_FIRST: |
| pixel = row[x << 2] << 24; |
| pixel |= row[(x << 2) + 1] << 16; |
| pixel |= row[(x << 2) + 2] << 8; |
| pixel |= row[(x << 2) + 3]; |
| break; |
| } |
| break; |
| default: |
| assert(0); |
| } |
| return pixel; |
| default: |
| assert(0); |
| } |
| } |
| |
| |
| xcb_image_t * |
| xcb_image_create_from_bitmap_data (uint8_t * data, |
| uint32_t width, |
| uint32_t height) |
| { |
| return xcb_image_create(width, height, XCB_IMAGE_FORMAT_XY_PIXMAP, |
| 8, 1, 1, 8, |
| XCB_IMAGE_ORDER_LSB_FIRST, |
| XCB_IMAGE_ORDER_LSB_FIRST, |
| 0, 0, data); |
| } |
| |
| |
| /* |
| * (Adapted from libX11.) |
| * |
| * xcb_create_pixmap_from_bitmap_data: Routine to make a pixmap of |
| * given depth from user supplied bitmap data. |
| * D is any drawable on the same screen that the pixmap will be used in. |
| * Data is a pointer to the bit data, and |
| * width & height give the size in bits of the pixmap. |
| * |
| * The following format is assumed for data: |
| * |
| * format=XY (will use XYPixmap for depth 1 and XYBitmap for larger) |
| * bit_order=LSBFirst |
| * padding=8 |
| * bitmap_unit=8 |
| */ |
| xcb_pixmap_t |
| xcb_create_pixmap_from_bitmap_data (xcb_connection_t * display, |
| xcb_drawable_t d, |
| uint8_t * data, |
| uint32_t width, |
| uint32_t height, |
| uint32_t depth, |
| uint32_t fg, |
| uint32_t bg, |
| xcb_gcontext_t * gcp) |
| { |
| xcb_pixmap_t pix; |
| xcb_image_t * image; |
| xcb_image_t * final_image; |
| xcb_gcontext_t gc; |
| uint32_t mask = 0; |
| xcb_params_gc_t gcv; |
| |
| image = xcb_image_create_from_bitmap_data(data, width, height); |
| if (!image) |
| return 0; |
| if (depth > 1) |
| image->format = XCB_IMAGE_FORMAT_XY_BITMAP; |
| final_image = xcb_image_native(display, image, 1); |
| if (!final_image) { |
| xcb_image_destroy(image); |
| return 0; |
| } |
| pix = xcb_generate_id(display); |
| xcb_create_pixmap(display, depth, pix, d, width, height); |
| gc = xcb_generate_id(display); |
| XCB_AUX_ADD_PARAM(&mask, &gcv, foreground, fg); |
| XCB_AUX_ADD_PARAM(&mask, &gcv, background, bg); |
| xcb_aux_create_gc(display, gc, pix, mask, &gcv); |
| xcb_image_put(display, pix, gc, final_image, 0, 0, 0); |
| if (final_image != image) |
| xcb_image_destroy(final_image); |
| xcb_image_destroy(image); |
| if (gcp) |
| *gcp = gc; |
| else |
| xcb_free_gc(display, gc); |
| return pix; |
| } |
| |
| |
| /* Thanks to Keith Packard <keithp@keithp.com> for this code */ |
| static void |
| swap_image(uint8_t * src, |
| uint32_t src_stride, |
| uint8_t * dst, |
| uint32_t dst_stride, |
| uint32_t height, |
| uint32_t byteswap, |
| int bitswap, |
| int nibbleswap) |
| { |
| while (height--) { |
| uint32_t s; |
| |
| for (s = 0; s < src_stride; s++) { |
| uint8_t b; |
| uint32_t d = s ^ byteswap; |
| |
| if (d > dst_stride) |
| continue; |
| |
| b = src[s]; |
| if (bitswap) |
| b = xcb_bit_reverse(b, 8); |
| if (nibbleswap) |
| b = (b << 4) | (b >> 4); |
| dst[d] = b; |
| } |
| src += src_stride; |
| dst += dst_stride; |
| } |
| } |
| |
| /* Which order are bytes in (low two bits), given |
| * code which accesses an image one byte at a time |
| */ |
| static uint32_t |
| byte_order(xcb_image_t *i) |
| { |
| uint32_t flip = i->byte_order == XCB_IMAGE_ORDER_MSB_FIRST; |
| |
| switch (i->bpp) { |
| default: |
| case 8: |
| return 0; |
| case 16: |
| return flip; |
| case 32: |
| return flip | (flip << 1); |
| } |
| } |
| |
| static uint32_t |
| bit_order(xcb_image_t *i) |
| { |
| uint32_t flip = i->byte_order != i->bit_order; |
| |
| switch (i->unit) { |
| default: |
| case 8: |
| return 0; |
| case 16: |
| return flip; |
| case 32: |
| return flip | (flip << 1); |
| } |
| } |
| |
| /* Convert from one byte order to another by flipping the |
| * low two bits of the byte index along a scanline |
| */ |
| static uint32_t |
| conversion_byte_swap(xcb_image_t *src, xcb_image_t *dst) |
| { |
| xcb_image_format_t ef = effective_format(src->format, src->bpp); |
| |
| /* src_ef == dst_ef in all callers of this function */ |
| if (ef == XCB_IMAGE_FORMAT_XY_PIXMAP) { |
| return bit_order(src) ^ bit_order(dst); |
| } else { |
| /* src_bpp == dst_bpp in all callers of this function */ |
| return byte_order(src) ^ byte_order(dst); |
| } |
| } |
| |
| xcb_image_t * |
| xcb_image_convert (xcb_image_t * src, |
| xcb_image_t * dst) |
| { |
| xcb_image_format_t ef = effective_format(src->format, src->bpp); |
| |
| /* Things will go horribly wrong here if a bad |
| image is passed in, so we check some things |
| up front just to be nice. */ |
| assert(image_format_valid(src)); |
| assert(image_format_valid(dst)); |
| |
| /* images must be the same size |
| * (yes, we could copy a sub-set) |
| */ |
| if (src->width != dst->width || |
| src->height != dst->height) |
| return 0; |
| |
| if (ef == effective_format(dst->format, dst->bpp) && |
| src->bpp == dst->bpp) |
| { |
| if (src->unit == dst->unit && |
| src->scanline_pad == dst->scanline_pad && |
| src->byte_order == dst->byte_order && |
| (ef == XCB_IMAGE_FORMAT_Z_PIXMAP || |
| src->bit_order == dst->bit_order)) { |
| memcpy(dst->data, src->data, src->size); |
| } else { |
| int bitswap = 0; |
| int nibbleswap = 0; |
| uint32_t byteswap = conversion_byte_swap(src, dst); |
| uint32_t height = src->height;; |
| |
| if (ef == XCB_IMAGE_FORMAT_Z_PIXMAP) { |
| if (src->bpp == 4 && src->byte_order != dst->byte_order) |
| nibbleswap = 1; |
| } else { |
| if (src->bit_order != dst->bit_order) |
| bitswap = 1; |
| height *= src->depth; |
| } |
| swap_image (src->data, src->stride, dst->data, dst->stride, |
| height, byteswap, bitswap, nibbleswap); |
| } |
| } |
| else |
| { |
| uint32_t x; |
| uint32_t y; |
| /* General case: Slow pixel copy. Should we optimize |
| Z24<->Z32 copies of either endianness? */ |
| for (y = 0; y < src->height; y++) { |
| for (x = 0; x < src->width; x++) { |
| uint32_t pixel = xcb_image_get_pixel(src, x, y); |
| xcb_image_put_pixel(dst, x, y, pixel); |
| } |
| } |
| } |
| return dst; |
| } |
| |
| xcb_image_t * |
| xcb_image_subimage(xcb_image_t * image, |
| uint32_t x, |
| uint32_t y, |
| uint32_t width, |
| uint32_t height, |
| void * base, |
| uint32_t bytes, |
| uint8_t * data) |
| { |
| int i, j; |
| xcb_image_t * result; |
| |
| if (x + width > image->width) |
| return 0; |
| if (y + height > image->height) |
| return 0; |
| result = xcb_image_create(width, height, image->format, |
| image->scanline_pad, image->depth, |
| image->bpp, image->unit, image->byte_order, |
| image->bit_order, |
| base, bytes, data); |
| if (!result) |
| return 0; |
| /* XXX FIXME For now, lose on performance. Sorry. */ |
| for (j = 0; j < height; j++) { |
| for (i = 0; i < width; i++) { |
| uint32_t pixel = xcb_image_get_pixel(image, x + i, y + j); |
| xcb_image_put_pixel(result, i, j, pixel); |
| } |
| } |
| return result; |
| } |