util: Helper function to determined whether two formats can be memcpy'ed.
[mesa.git] / src / gallium / auxiliary / util / u_format.c
1 /**************************************************************************
2 *
3 * Copyright 2010 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28 /**
29 * @file
30 * Pixel format accessor functions.
31 *
32 * @author Jose Fonseca <jfonseca@vmware.com>
33 */
34
35 #include "u_math.h"
36 #include "u_memory.h"
37 #include "u_rect.h"
38 #include "u_format.h"
39
40
41 void
42 util_format_read_4f(enum pipe_format format,
43 float *dst, unsigned dst_stride,
44 const void *src, unsigned src_stride,
45 unsigned x, unsigned y, unsigned w, unsigned h)
46 {
47 const struct util_format_description *format_desc;
48 const uint8_t *src_row;
49 float *dst_row;
50
51 format_desc = util_format_description(format);
52
53 assert(x % format_desc->block.width == 0);
54 assert(y % format_desc->block.height == 0);
55
56 src_row = (const uint8_t *)src + y*src_stride + x*(format_desc->block.bits/8);
57 dst_row = dst;
58
59 format_desc->unpack_rgba_float(dst_row, dst_stride, src_row, src_stride, w, h);
60 }
61
62
63 void
64 util_format_write_4f(enum pipe_format format,
65 const float *src, unsigned src_stride,
66 void *dst, unsigned dst_stride,
67 unsigned x, unsigned y, unsigned w, unsigned h)
68 {
69 const struct util_format_description *format_desc;
70 uint8_t *dst_row;
71 const float *src_row;
72
73 format_desc = util_format_description(format);
74
75 assert(x % format_desc->block.width == 0);
76 assert(y % format_desc->block.height == 0);
77
78 dst_row = (uint8_t *)dst + y*dst_stride + x*(format_desc->block.bits/8);
79 src_row = src;
80
81 format_desc->pack_rgba_float(dst_row, dst_stride, src_row, src_stride, w, h);
82 }
83
84
85 void
86 util_format_read_4ub(enum pipe_format format, uint8_t *dst, unsigned dst_stride, const void *src, unsigned src_stride, unsigned x, unsigned y, unsigned w, unsigned h)
87 {
88 const struct util_format_description *format_desc;
89 const uint8_t *src_row;
90 uint8_t *dst_row;
91
92 format_desc = util_format_description(format);
93
94 assert(x % format_desc->block.width == 0);
95 assert(y % format_desc->block.height == 0);
96
97 src_row = (const uint8_t *)src + y*src_stride + x*(format_desc->block.bits/8);
98 dst_row = dst;
99
100 format_desc->unpack_rgba_8unorm(dst_row, dst_stride, src_row, src_stride, w, h);
101 }
102
103
104 void
105 util_format_write_4ub(enum pipe_format format, const uint8_t *src, unsigned src_stride, void *dst, unsigned dst_stride, unsigned x, unsigned y, unsigned w, unsigned h)
106 {
107 const struct util_format_description *format_desc;
108 uint8_t *dst_row;
109 const uint8_t *src_row;
110
111 format_desc = util_format_description(format);
112
113 assert(x % format_desc->block.width == 0);
114 assert(y % format_desc->block.height == 0);
115
116 dst_row = (uint8_t *)dst + y*dst_stride + x*(format_desc->block.bits/8);
117 src_row = src;
118
119 format_desc->pack_rgba_8unorm(dst_row, dst_stride, src_row, src_stride, w, h);
120 }
121
122
123 boolean
124 util_is_format_compatible(const struct util_format_description *src_desc,
125 const struct util_format_description *dst_desc)
126 {
127 unsigned chan;
128
129 if (src_desc->format == dst_desc->format) {
130 return TRUE;
131 }
132
133 if (src_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN ||
134 dst_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN) {
135 return FALSE;
136 }
137
138 if (src_desc->block.bits != dst_desc->block.bits ||
139 src_desc->nr_channels != dst_desc->nr_channels ||
140 src_desc->colorspace != dst_desc->colorspace) {
141 return FALSE;
142 }
143
144 for (chan = 0; chan < 4; ++chan) {
145 if (src_desc->channel[chan].size !=
146 dst_desc->channel[chan].size) {
147 return FALSE;
148 }
149 }
150
151 for (chan = 0; chan < 4; ++chan) {
152 enum util_format_swizzle swizzle = dst_desc->swizzle[chan];
153
154 if (swizzle < 4) {
155 if (src_desc->swizzle[chan] != swizzle) {
156 return FALSE;
157 }
158 if ((src_desc->channel[swizzle].type !=
159 dst_desc->channel[swizzle].type) ||
160 (src_desc->channel[swizzle].normalized !=
161 dst_desc->channel[swizzle].normalized)) {
162 return FALSE;
163 }
164 }
165 }
166
167 return TRUE;
168 }
169
170
171 boolean
172 util_format_fits_8unorm(const struct util_format_description *format_desc)
173 {
174 unsigned chan;
175
176 switch (format_desc->layout) {
177
178 case UTIL_FORMAT_LAYOUT_S3TC:
179 case UTIL_FORMAT_LAYOUT_RGTC:
180 /*
181 * These are straight forward.
182 */
183
184 return TRUE;
185
186 case UTIL_FORMAT_LAYOUT_PLAIN:
187 /*
188 * For these we can find a generic rule.
189 */
190
191 for (chan = 0; chan < format_desc->nr_channels; ++chan) {
192 switch (format_desc->channel[chan].type) {
193 case UTIL_FORMAT_TYPE_VOID:
194 break;
195 case UTIL_FORMAT_TYPE_UNSIGNED:
196 if (!format_desc->channel[chan].normalized ||
197 format_desc->channel[chan].size > 8) {
198 return FALSE;
199 }
200 break;
201 default:
202 return FALSE;
203 }
204 }
205 return TRUE;
206
207 default:
208 /*
209 * Handle all others on a case by case basis.
210 */
211
212 switch (format_desc->format) {
213 case PIPE_FORMAT_R1_UNORM:
214 case PIPE_FORMAT_UYVY:
215 case PIPE_FORMAT_YUYV:
216 case PIPE_FORMAT_R8G8_B8G8_UNORM:
217 case PIPE_FORMAT_G8R8_G8B8_UNORM:
218 return TRUE;
219
220 default:
221 return FALSE;
222 }
223 }
224 }
225
226
227 void
228 util_format_translate(enum pipe_format dst_format,
229 void *dst, unsigned dst_stride,
230 unsigned dst_x, unsigned dst_y,
231 enum pipe_format src_format,
232 const void *src, unsigned src_stride,
233 unsigned src_x, unsigned src_y,
234 unsigned width, unsigned height)
235 {
236 const struct util_format_description *dst_format_desc;
237 const struct util_format_description *src_format_desc;
238 uint8_t *dst_row;
239 const uint8_t *src_row;
240 unsigned x_step, y_step;
241 unsigned dst_step;
242 unsigned src_step;
243
244 dst_format_desc = util_format_description(dst_format);
245 src_format_desc = util_format_description(src_format);
246
247 if (util_is_format_compatible(src_format_desc, dst_format_desc)) {
248 /*
249 * Trivial case.
250 */
251
252 util_copy_rect(dst, dst_format, dst_stride, dst_x, dst_y,
253 width, height, src, (int)src_stride,
254 src_x, src_y);
255 return;
256 }
257
258 assert(dst_x % dst_format_desc->block.width == 0);
259 assert(dst_y % dst_format_desc->block.height == 0);
260 assert(src_x % src_format_desc->block.width == 0);
261 assert(src_y % src_format_desc->block.height == 0);
262
263 dst_row = (uint8_t *)dst + dst_y*dst_stride + dst_x*(dst_format_desc->block.bits/8);
264 src_row = (const uint8_t *)src + src_y*src_stride + src_x*(src_format_desc->block.bits/8);
265
266 /*
267 * This works because all pixel formats have pixel blocks with power of two
268 * sizes.
269 */
270
271 y_step = MAX2(dst_format_desc->block.height, src_format_desc->block.height);
272 x_step = MAX2(dst_format_desc->block.width, src_format_desc->block.width);
273 assert(y_step % dst_format_desc->block.height == 0);
274 assert(y_step % src_format_desc->block.height == 0);
275
276 dst_step = y_step / dst_format_desc->block.height * dst_stride;
277 src_step = y_step / src_format_desc->block.height * src_stride;
278
279 /*
280 * TODO: double formats will loose precision
281 * TODO: Add a special case for formats that are mere swizzles of each other
282 */
283
284 if (util_format_fits_8unorm(src_format_desc) ||
285 util_format_fits_8unorm(dst_format_desc)) {
286 unsigned tmp_stride;
287 uint8_t *tmp_row;
288
289 tmp_stride = MAX2(width, x_step) * 4 * sizeof *tmp_row;
290 tmp_row = MALLOC(y_step * tmp_stride);
291 if (!tmp_row)
292 return;
293
294 while (height >= y_step) {
295 src_format_desc->unpack_rgba_8unorm(tmp_row, tmp_stride, src_row, src_stride, width, y_step);
296 dst_format_desc->pack_rgba_8unorm(dst_row, dst_stride, tmp_row, tmp_stride, width, y_step);
297
298 dst_row += dst_step;
299 src_row += src_step;
300 height -= y_step;
301 }
302
303 if (height) {
304 src_format_desc->unpack_rgba_8unorm(tmp_row, tmp_stride, src_row, src_stride, width, height);
305 dst_format_desc->pack_rgba_8unorm(dst_row, dst_stride, tmp_row, tmp_stride, width, height);
306 }
307
308 FREE(tmp_row);
309 }
310 else {
311 unsigned tmp_stride;
312 float *tmp_row;
313
314 tmp_stride = MAX2(width, x_step) * 4 * sizeof *tmp_row;
315 tmp_row = MALLOC(y_step * tmp_stride);
316 if (!tmp_row)
317 return;
318
319 while (height >= y_step) {
320 src_format_desc->unpack_rgba_float(tmp_row, tmp_stride, src_row, src_stride, width, y_step);
321 dst_format_desc->pack_rgba_float(dst_row, dst_stride, tmp_row, tmp_stride, width, y_step);
322
323 dst_row += dst_step;
324 src_row += src_step;
325 height -= y_step;
326 }
327
328 if (height) {
329 src_format_desc->unpack_rgba_float(tmp_row, tmp_stride, src_row, src_stride, width, height);
330 dst_format_desc->pack_rgba_float(dst_row, dst_stride, tmp_row, tmp_stride, width, height);
331 }
332
333 FREE(tmp_row);
334 }
335 }