Merge branch 'master' of ssh://git.freedesktop.org/git/mesa/mesa into pipe-video
[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 /*
177 * After linearized sRGB values require more than 8bits.
178 */
179
180 if (format_desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) {
181 return FALSE;
182 }
183
184 switch (format_desc->layout) {
185
186 case UTIL_FORMAT_LAYOUT_S3TC:
187 case UTIL_FORMAT_LAYOUT_RGTC:
188 /*
189 * These are straight forward.
190 */
191
192 return TRUE;
193
194 case UTIL_FORMAT_LAYOUT_PLAIN:
195 /*
196 * For these we can find a generic rule.
197 */
198
199 for (chan = 0; chan < format_desc->nr_channels; ++chan) {
200 switch (format_desc->channel[chan].type) {
201 case UTIL_FORMAT_TYPE_VOID:
202 break;
203 case UTIL_FORMAT_TYPE_UNSIGNED:
204 if (!format_desc->channel[chan].normalized ||
205 format_desc->channel[chan].size > 8) {
206 return FALSE;
207 }
208 break;
209 default:
210 return FALSE;
211 }
212 }
213 return TRUE;
214
215 default:
216 /*
217 * Handle all others on a case by case basis.
218 */
219
220 switch (format_desc->format) {
221 case PIPE_FORMAT_R1_UNORM:
222 case PIPE_FORMAT_UYVY:
223 case PIPE_FORMAT_YUYV:
224 case PIPE_FORMAT_R8G8_B8G8_UNORM:
225 case PIPE_FORMAT_G8R8_G8B8_UNORM:
226 return TRUE;
227
228 default:
229 return FALSE;
230 }
231 }
232 }
233
234
235 void
236 util_format_translate(enum pipe_format dst_format,
237 void *dst, unsigned dst_stride,
238 unsigned dst_x, unsigned dst_y,
239 enum pipe_format src_format,
240 const void *src, unsigned src_stride,
241 unsigned src_x, unsigned src_y,
242 unsigned width, unsigned height)
243 {
244 const struct util_format_description *dst_format_desc;
245 const struct util_format_description *src_format_desc;
246 uint8_t *dst_row;
247 const uint8_t *src_row;
248 unsigned x_step, y_step;
249 unsigned dst_step;
250 unsigned src_step;
251
252 dst_format_desc = util_format_description(dst_format);
253 src_format_desc = util_format_description(src_format);
254
255 if (util_is_format_compatible(src_format_desc, dst_format_desc)) {
256 /*
257 * Trivial case.
258 */
259
260 util_copy_rect(dst, dst_format, dst_stride, dst_x, dst_y,
261 width, height, src, (int)src_stride,
262 src_x, src_y);
263 return;
264 }
265
266 assert(dst_x % dst_format_desc->block.width == 0);
267 assert(dst_y % dst_format_desc->block.height == 0);
268 assert(src_x % src_format_desc->block.width == 0);
269 assert(src_y % src_format_desc->block.height == 0);
270
271 dst_row = (uint8_t *)dst + dst_y*dst_stride + dst_x*(dst_format_desc->block.bits/8);
272 src_row = (const uint8_t *)src + src_y*src_stride + src_x*(src_format_desc->block.bits/8);
273
274 /*
275 * This works because all pixel formats have pixel blocks with power of two
276 * sizes.
277 */
278
279 y_step = MAX2(dst_format_desc->block.height, src_format_desc->block.height);
280 x_step = MAX2(dst_format_desc->block.width, src_format_desc->block.width);
281 assert(y_step % dst_format_desc->block.height == 0);
282 assert(y_step % src_format_desc->block.height == 0);
283
284 dst_step = y_step / dst_format_desc->block.height * dst_stride;
285 src_step = y_step / src_format_desc->block.height * src_stride;
286
287 /*
288 * TODO: double formats will loose precision
289 * TODO: Add a special case for formats that are mere swizzles of each other
290 */
291
292 if (util_format_fits_8unorm(src_format_desc) ||
293 util_format_fits_8unorm(dst_format_desc)) {
294 unsigned tmp_stride;
295 uint8_t *tmp_row;
296
297 tmp_stride = MAX2(width, x_step) * 4 * sizeof *tmp_row;
298 tmp_row = MALLOC(y_step * tmp_stride);
299 if (!tmp_row)
300 return;
301
302 while (height >= y_step) {
303 src_format_desc->unpack_rgba_8unorm(tmp_row, tmp_stride, src_row, src_stride, width, y_step);
304 dst_format_desc->pack_rgba_8unorm(dst_row, dst_stride, tmp_row, tmp_stride, width, y_step);
305
306 dst_row += dst_step;
307 src_row += src_step;
308 height -= y_step;
309 }
310
311 if (height) {
312 src_format_desc->unpack_rgba_8unorm(tmp_row, tmp_stride, src_row, src_stride, width, height);
313 dst_format_desc->pack_rgba_8unorm(dst_row, dst_stride, tmp_row, tmp_stride, width, height);
314 }
315
316 FREE(tmp_row);
317 }
318 else {
319 unsigned tmp_stride;
320 float *tmp_row;
321
322 tmp_stride = MAX2(width, x_step) * 4 * sizeof *tmp_row;
323 tmp_row = MALLOC(y_step * tmp_stride);
324 if (!tmp_row)
325 return;
326
327 while (height >= y_step) {
328 src_format_desc->unpack_rgba_float(tmp_row, tmp_stride, src_row, src_stride, width, y_step);
329 dst_format_desc->pack_rgba_float(dst_row, dst_stride, tmp_row, tmp_stride, width, y_step);
330
331 dst_row += dst_step;
332 src_row += src_step;
333 height -= y_step;
334 }
335
336 if (height) {
337 src_format_desc->unpack_rgba_float(tmp_row, tmp_stride, src_row, src_stride, width, height);
338 dst_format_desc->pack_rgba_float(dst_row, dst_stride, tmp_row, tmp_stride, width, height);
339 }
340
341 FREE(tmp_row);
342 }
343 }