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