1/* 2 * Copyright 2009, 2010 Red Hat, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * on the rights to use, copy, modify, merge, publish, distribute, sub 8 * license, and/or sell copies of the Software, and to permit persons to whom 9 * the Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 * 22 */ 23/** \file QXLImage.c 24 * \author Søren Sandmann <sandmann@redhat.com> 25 */ 26 27#ifdef HAVE_CONFIG_H 28#include "config.h" 29#endif 30 31#include <string.h> 32#include <assert.h> 33#include <stdlib.h> 34 35#include <spice/macros.h> 36 37#include "qxl.h" 38#include "murmurhash3.h" 39 40static unsigned int 41hash_and_copy (const uint8_t *src, int src_stride, 42 uint8_t *dest, int dest_stride, 43 int bytes_per_pixel, int width, int height, 44 uint32_t hash) 45{ 46 int i; 47 48 for (i = 0; i < height; ++i) 49 { 50 const uint8_t *src_line = src + i * src_stride; 51 uint8_t *dest_line = dest + i * dest_stride; 52 int n_bytes = width * bytes_per_pixel; 53 if (n_bytes > src_stride) 54 n_bytes = src_stride; 55 56 if (dest) 57 memcpy (dest_line, src_line, n_bytes); 58 59 MurmurHash3_x86_32 (src_line, n_bytes, hash, &hash); 60 } 61 62 return hash; 63} 64 65struct qxl_bo * 66qxl_image_create (qxl_screen_t *qxl, const uint8_t *data, 67 int x, int y, int width, int height, 68 int stride, int Bpp, Bool fallback) 69{ 70 uint32_t hash; 71 struct QXLImage *image; 72 struct qxl_bo *head_bo, *tail_bo; 73 struct qxl_bo *image_bo; 74 int dest_stride = (width * Bpp + 3) & (~3); 75 int h; 76 int chunk_size; 77 78 data += y * stride + x * Bpp; 79 80#if 0 81 ErrorF ("Must create new image of size %d %d\n", width, height); 82#endif 83 84 /* Chunk */ 85 86 /* FIXME: Check integer overflow */ 87 88 head_bo = tail_bo = NULL; 89 90 hash = 0; 91 h = height; 92 93 chunk_size = MAX (512 * 512, dest_stride); 94 95#ifdef XF86DRM_MODE 96 /* ensure we will not create too many pieces and overflow 97 * the command buffer (MAX_RELOCS). if so, increase the chunk_size. 98 * each loop creates at least 2 cmd buffer entries, and 99 * we have to leave room when we're done. 100 */ 101 if (height / (chunk_size / dest_stride) > (MAX_RELOCS / 4)) { 102 chunk_size = height / (MAX_RELOCS/4) * dest_stride; 103#if 0 104 ErrorF ("adjusted chunk_size to %d\n", chunk_size); 105#endif 106 } 107#endif 108 109 while (h) 110 { 111 int n_lines = MIN ((chunk_size / dest_stride), h); 112 struct qxl_bo *bo = qxl->bo_funcs->bo_alloc (qxl, sizeof (QXLDataChunk) + n_lines * dest_stride, "image data"); 113 114 QXLDataChunk *chunk = qxl->bo_funcs->bo_map(bo); 115 chunk->data_size = n_lines * dest_stride; 116 hash = hash_and_copy (data, stride, 117 chunk->data, dest_stride, 118 Bpp, width, n_lines, hash); 119 120 if (tail_bo) 121 { 122 qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDataChunk, next_chunk), 123 tail_bo, bo); 124 qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDataChunk, prev_chunk), 125 bo, tail_bo); 126 127 chunk->next_chunk = 0; 128 129 tail_bo = bo; 130 } 131 else 132 { 133 head_bo = tail_bo = bo; 134 chunk->next_chunk = 0; 135 chunk->prev_chunk = 0; 136 } 137 138 qxl->bo_funcs->bo_unmap(bo); 139 if (bo != head_bo) 140 qxl->bo_funcs->bo_decref(qxl, bo); 141 data += n_lines * stride; 142 h -= n_lines; 143 } 144 145 /* Image */ 146 image_bo = qxl->bo_funcs->bo_alloc (qxl, sizeof *image, "image struct"); 147 image = qxl->bo_funcs->bo_map(image_bo); 148 149 image->descriptor.id = 0; 150 image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; 151 152 image->descriptor.flags = 0; 153 image->descriptor.width = width; 154 image->descriptor.height = height; 155 156 if (Bpp == 2) 157 { 158 image->bitmap.format = SPICE_BITMAP_FMT_16BIT; 159 } 160 else if (Bpp == 1) 161 { 162 image->bitmap.format = SPICE_BITMAP_FMT_8BIT_A; 163 } 164 else if (Bpp == 4) 165 { 166 image->bitmap.format = SPICE_BITMAP_FMT_RGBA; 167 } 168 else 169 { 170 abort(); 171 } 172 173 image->bitmap.flags = SPICE_BITMAP_FLAGS_TOP_DOWN; 174 image->bitmap.x = width; 175 image->bitmap.y = height; 176 image->bitmap.stride = dest_stride; 177 image->bitmap.palette = 0; 178 qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLImage, bitmap.data), 179 image_bo, head_bo); 180 181 qxl->bo_funcs->bo_decref(qxl, head_bo); 182 /* Add to hash table if caching is enabled */ 183 if ((fallback && qxl->enable_fallback_cache) || 184 (!fallback && qxl->enable_image_cache)) 185 { 186 image->descriptor.id = hash; 187 image->descriptor.flags = QXL_IMAGE_CACHE; 188#if 0 189 ErrorF ("added with hash %u\n", hash); 190#endif 191 } 192 193 qxl->bo_funcs->bo_unmap(image_bo); 194 return image_bo; 195} 196 197void 198qxl_image_destroy (qxl_screen_t *qxl, 199 struct qxl_bo *image_bo) 200{ 201 struct QXLImage *image; 202 uint64_t chunk, prev_chunk; 203 204 image = qxl->bo_funcs->bo_map(image_bo); 205 qxl->bo_funcs->bo_unmap(image_bo); 206 207 image = qxl->bo_funcs->bo_map(image_bo); 208 chunk = image->bitmap.data; 209 while (chunk) 210 { 211 struct qxl_bo *bo; 212 struct QXLDataChunk *virtual; 213 214 bo = qxl_ums_lookup_phy_addr(qxl, chunk); 215 assert(bo); 216 virtual = qxl->bo_funcs->bo_map(bo); 217 chunk = virtual->next_chunk; 218 prev_chunk = virtual->prev_chunk; 219 220 qxl->bo_funcs->bo_unmap(bo); 221 qxl->bo_funcs->bo_decref (qxl, bo); 222 if (prev_chunk) { 223 bo = qxl_ums_lookup_phy_addr(qxl, prev_chunk); 224 assert(bo); 225 qxl->bo_funcs->bo_decref (qxl, bo); 226 } 227 } 228 qxl->bo_funcs->bo_unmap(image_bo); 229 qxl->bo_funcs->bo_decref (qxl, image_bo); 230} 231