Merge branch '7.8'
[mesa.git] / src / gallium / state_trackers / vega / image.c
1 /**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
15 * of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 **************************************************************************/
26
27 #include "image.h"
28
29 #include "vg_translate.h"
30 #include "vg_context.h"
31 #include "matrix.h"
32 #include "renderer.h"
33 #include "util_array.h"
34 #include "api_consts.h"
35 #include "shaders_cache.h"
36 #include "shader.h"
37
38 #include "pipe/p_context.h"
39 #include "pipe/p_screen.h"
40 #include "util/u_inlines.h"
41 #include "util/u_blit.h"
42 #include "util/u_format.h"
43 #include "util/u_tile.h"
44 #include "util/u_memory.h"
45 #include "util/u_math.h"
46
47 static enum pipe_format vg_format_to_pipe(VGImageFormat format)
48 {
49 switch(format) {
50 case VG_sRGB_565:
51 return PIPE_FORMAT_B5G6R5_UNORM;
52 case VG_sRGBA_5551:
53 return PIPE_FORMAT_B5G5R5A1_UNORM;
54 case VG_sRGBA_4444:
55 return PIPE_FORMAT_B4G4R4A4_UNORM;
56 case VG_sL_8:
57 case VG_lL_8:
58 return PIPE_FORMAT_L8_UNORM;
59 case VG_BW_1:
60 return PIPE_FORMAT_B8G8R8A8_UNORM;
61 case VG_A_8:
62 return PIPE_FORMAT_A8_UNORM;
63 #ifdef OPENVG_VERSION_1_1
64 case VG_A_1:
65 case VG_A_4:
66 return PIPE_FORMAT_A8_UNORM;
67 #endif
68 default:
69 return PIPE_FORMAT_B8G8R8A8_UNORM;
70 }
71 }
72
73 static INLINE void vg_sync_size(VGfloat *src_loc, VGfloat *dst_loc)
74 {
75 src_loc[2] = MIN2(src_loc[2], dst_loc[2]);
76 src_loc[3] = MIN2(src_loc[3], dst_loc[3]);
77 dst_loc[2] = src_loc[2];
78 dst_loc[3] = src_loc[3];
79 }
80
81
82 static void vg_copy_texture(struct vg_context *ctx,
83 struct pipe_texture *dst, VGint dx, VGint dy,
84 struct pipe_texture *src, VGint sx, VGint sy,
85 VGint width, VGint height)
86 {
87 VGfloat dst_loc[4], src_loc[4];
88 VGfloat dst_bounds[4], src_bounds[4];
89 VGfloat src_shift[4], dst_shift[4], shift[4];
90
91 dst_loc[0] = dx;
92 dst_loc[1] = dy;
93 dst_loc[2] = width;
94 dst_loc[3] = height;
95 dst_bounds[0] = 0.f;
96 dst_bounds[1] = 0.f;
97 dst_bounds[2] = dst->width0;
98 dst_bounds[3] = dst->height0;
99
100 src_loc[0] = sx;
101 src_loc[1] = sy;
102 src_loc[2] = width;
103 src_loc[3] = height;
104 src_bounds[0] = 0.f;
105 src_bounds[1] = 0.f;
106 src_bounds[2] = src->width0;
107 src_bounds[3] = src->height0;
108
109 vg_bound_rect(src_loc, src_bounds, src_shift);
110 vg_bound_rect(dst_loc, dst_bounds, dst_shift);
111 shift[0] = src_shift[0] - dst_shift[0];
112 shift[1] = src_shift[1] - dst_shift[1];
113
114 if (shift[0] < 0)
115 vg_shift_rectx(src_loc, src_bounds, -shift[0]);
116 else
117 vg_shift_rectx(dst_loc, dst_bounds, shift[0]);
118
119 if (shift[1] < 0)
120 vg_shift_recty(src_loc, src_bounds, -shift[1]);
121 else
122 vg_shift_recty(dst_loc, dst_bounds, shift[1]);
123
124 vg_sync_size(src_loc, dst_loc);
125
126 if (src_loc[2] >= 0 && src_loc[3] >= 0 &&
127 dst_loc[2] >= 0 && dst_loc[3] >= 0) {
128 renderer_copy_texture(ctx->renderer,
129 src,
130 src_loc[0],
131 src_loc[1] + src_loc[3],
132 src_loc[0] + src_loc[2],
133 src_loc[1],
134 dst,
135 dst_loc[0],
136 dst_loc[1] + dst_loc[3],
137 dst_loc[0] + dst_loc[2],
138 dst_loc[1]);
139 }
140
141 }
142
143 void vg_copy_surface(struct vg_context *ctx,
144 struct pipe_surface *dst, VGint dx, VGint dy,
145 struct pipe_surface *src, VGint sx, VGint sy,
146 VGint width, VGint height)
147 {
148 VGfloat dst_loc[4], src_loc[4];
149 VGfloat dst_bounds[4], src_bounds[4];
150 VGfloat src_shift[4], dst_shift[4], shift[4];
151
152 dst_loc[0] = dx;
153 dst_loc[1] = dy;
154 dst_loc[2] = width;
155 dst_loc[3] = height;
156 dst_bounds[0] = 0.f;
157 dst_bounds[1] = 0.f;
158 dst_bounds[2] = dst->width;
159 dst_bounds[3] = dst->height;
160
161 src_loc[0] = sx;
162 src_loc[1] = sy;
163 src_loc[2] = width;
164 src_loc[3] = height;
165 src_bounds[0] = 0.f;
166 src_bounds[1] = 0.f;
167 src_bounds[2] = src->width;
168 src_bounds[3] = src->height;
169
170 vg_bound_rect(src_loc, src_bounds, src_shift);
171 vg_bound_rect(dst_loc, dst_bounds, dst_shift);
172 shift[0] = src_shift[0] - dst_shift[0];
173 shift[1] = src_shift[1] - dst_shift[1];
174
175 if (shift[0] < 0)
176 vg_shift_rectx(src_loc, src_bounds, -shift[0]);
177 else
178 vg_shift_rectx(dst_loc, dst_bounds, shift[0]);
179
180 if (shift[1] < 0)
181 vg_shift_recty(src_loc, src_bounds, -shift[1]);
182 else
183 vg_shift_recty(dst_loc, dst_bounds, shift[1]);
184
185 vg_sync_size(src_loc, dst_loc);
186
187 if (src_loc[2] > 0 && src_loc[3] > 0 &&
188 dst_loc[2] > 0 && dst_loc[3] > 0) {
189 if (src == dst)
190 renderer_copy_surface(ctx->renderer,
191 src,
192 src_loc[0],
193 src->height - (src_loc[1] + src_loc[3]),
194 src_loc[0] + src_loc[2],
195 src->height - src_loc[1],
196 dst,
197 dst_loc[0],
198 dst->height - (dst_loc[1] + dst_loc[3]),
199 dst_loc[0] + dst_loc[2],
200 dst->height - dst_loc[1],
201 0, 0);
202 else
203 renderer_copy_surface(ctx->renderer,
204 src,
205 src_loc[0],
206 src->height - src_loc[1],
207 src_loc[0] + src_loc[2],
208 src->height - (src_loc[1] + src_loc[3]),
209 dst,
210 dst_loc[0],
211 dst->height - (dst_loc[1] + dst_loc[3]),
212 dst_loc[0] + dst_loc[2],
213 dst->height - dst_loc[1],
214 0, 0);
215 }
216
217 }
218
219 static struct pipe_texture *image_texture(struct vg_image *img)
220 {
221 struct pipe_texture *tex = img->texture;
222 return tex;
223 }
224
225
226 static void image_cleari(struct vg_image *img, VGint clear_colori,
227 VGint x, VGint y, VGint width, VGint height)
228 {
229 VGint *clearbuf;
230 VGint i;
231 VGfloat dwidth, dheight;
232
233 clearbuf = malloc(sizeof(VGint)*width*height);
234 for (i = 0; i < width*height; ++i)
235 clearbuf[i] = clear_colori;
236
237 dwidth = MIN2(width, img->width);
238 dheight = MIN2(height, img->height);
239
240 image_sub_data(img, clearbuf, width * sizeof(VGint),
241 VG_sRGBA_8888,
242 x, y, dwidth, dheight);
243 free(clearbuf);
244 }
245
246 struct vg_image * image_create(VGImageFormat format,
247 VGint width, VGint height)
248 {
249 struct vg_context *ctx = vg_current_context();
250 struct vg_image *image = CALLOC_STRUCT(vg_image);
251 enum pipe_format pformat = vg_format_to_pipe(format);
252 struct pipe_texture pt, *newtex;
253 struct pipe_screen *screen = ctx->pipe->screen;
254
255 vg_init_object(&image->base, ctx, VG_OBJECT_IMAGE);
256
257 image->format = format;
258 image->width = width;
259 image->height = height;
260
261 image->sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
262 image->sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
263 image->sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
264 image->sampler.min_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
265 image->sampler.mag_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
266 image->sampler.normalized_coords = 1;
267
268 assert(screen->is_format_supported(screen, pformat, PIPE_TEXTURE_2D,
269 PIPE_TEXTURE_USAGE_SAMPLER, 0));
270
271 memset(&pt, 0, sizeof(pt));
272 pt.target = PIPE_TEXTURE_2D;
273 pt.format = pformat;
274 pt.last_level = 0;
275 pt.width0 = width;
276 pt.height0 = height;
277 pt.depth0 = 1;
278 pt.tex_usage = PIPE_TEXTURE_USAGE_SAMPLER;
279
280 newtex = screen->texture_create(screen, &pt);
281
282 debug_assert(newtex);
283
284 image->texture = newtex;
285
286 vg_context_add_object(ctx, VG_OBJECT_IMAGE, image);
287
288 image_cleari(image, 0, 0, 0, image->width, image->height);
289 return image;
290 }
291
292 void image_destroy(struct vg_image *img)
293 {
294 struct vg_context *ctx = vg_current_context();
295 vg_context_remove_object(ctx, VG_OBJECT_IMAGE, img);
296
297
298 if (img->parent) {
299 /* remove img from the parent child array */
300 int idx;
301 struct vg_image **array =
302 (struct vg_image **)img->parent->children_array->data;
303
304 for (idx = 0; idx < img->parent->children_array->num_elements; ++idx) {
305 struct vg_image *child = array[idx];
306 if (child == img) {
307 break;
308 }
309 }
310 debug_assert(idx < img->parent->children_array->num_elements);
311 array_remove_element(img->parent->children_array, idx);
312 }
313
314 if (img->children_array && img->children_array->num_elements) {
315 /* reparent the children */
316 VGint i;
317 struct vg_image *parent = img->parent;
318 struct vg_image **children =
319 (struct vg_image **)img->children_array->data;
320 if (!parent) {
321 VGint min_x = children[0]->x;
322 parent = children[0];
323
324 for (i = 1; i < img->children_array->num_elements; ++i) {
325 struct vg_image *child = children[i];
326 if (child->x < min_x) {
327 parent = child;
328 }
329 }
330 }
331
332 for (i = 0; i < img->children_array->num_elements; ++i) {
333 struct vg_image *child = children[i];
334 if (child != parent) {
335 child->parent = parent;
336 if (!parent->children_array) {
337 parent->children_array = array_create(
338 sizeof(struct vg_image*));
339 }
340 array_append_data(parent->children_array,
341 &child, 1);
342 } else
343 child->parent = NULL;
344 }
345 array_destroy(img->children_array);
346 }
347
348 pipe_texture_reference(&img->texture, NULL);
349 free(img);
350 }
351
352 void image_clear(struct vg_image *img,
353 VGint x, VGint y, VGint width, VGint height)
354 {
355 struct vg_context *ctx = vg_current_context();
356 VGfloat *clear_colorf = ctx->state.vg.clear_color;
357 VGubyte r, g, b ,a;
358 VGint clear_colori;
359 /* FIXME: this is very nasty */
360 r = float_to_ubyte(clear_colorf[0]);
361 g = float_to_ubyte(clear_colorf[1]);
362 b = float_to_ubyte(clear_colorf[2]);
363 a = float_to_ubyte(clear_colorf[3]);
364 clear_colori = r << 24 | g << 16 | b << 8 | a;
365 image_cleari(img, clear_colori, x, y, width, height);
366 }
367
368 void image_sub_data(struct vg_image *image,
369 const void * data,
370 VGint dataStride,
371 VGImageFormat dataFormat,
372 VGint x, VGint y,
373 VGint width, VGint height)
374 {
375 const VGint yStep = 1;
376 VGubyte *src = (VGubyte *)data;
377 VGfloat temp[VEGA_MAX_IMAGE_WIDTH][4];
378 VGfloat *df = (VGfloat*)temp;
379 VGint i;
380 struct vg_context *ctx = vg_current_context();
381 struct pipe_context *pipe = ctx->pipe;
382 struct pipe_texture *texture = image_texture(image);
383 VGint xoffset = 0, yoffset = 0;
384
385 if (x < 0) {
386 xoffset -= x;
387 width += x;
388 x = 0;
389 }
390 if (y < 0) {
391 yoffset -= y;
392 height += y;
393 y = 0;
394 }
395
396 if (width <= 0 || height <= 0) {
397 vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
398 return;
399 }
400
401 if (x > image->width || y > image->width) {
402 vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
403 return;
404 }
405
406 if (x + width > image->width) {
407 width = image->width - x;
408 }
409
410 if (y + height > image->height) {
411 height = image->height - y;
412 }
413
414 { /* upload color_data */
415 struct pipe_transfer *transfer = pipe->get_tex_transfer(
416 pipe, texture, 0, 0, 0,
417 PIPE_TRANSFER_WRITE, 0, 0, texture->width0, texture->height0);
418 src += (dataStride * yoffset);
419 for (i = 0; i < height; i++) {
420 _vega_unpack_float_span_rgba(ctx, width, xoffset, src, dataFormat, temp);
421 pipe_put_tile_rgba(pipe, transfer, x+image->x, y+image->y, width, 1, df);
422 y += yStep;
423 src += dataStride;
424 }
425 pipe->tex_transfer_destroy(pipe, transfer);
426 }
427 }
428
429 void image_get_sub_data(struct vg_image * image,
430 void * data,
431 VGint dataStride,
432 VGImageFormat dataFormat,
433 VGint sx, VGint sy,
434 VGint width, VGint height)
435 {
436 struct vg_context *ctx = vg_current_context();
437 struct pipe_context *pipe = ctx->pipe;
438 VGfloat temp[VEGA_MAX_IMAGE_WIDTH][4];
439 VGfloat *df = (VGfloat*)temp;
440 VGint y = 0, yStep = 1;
441 VGint i;
442 VGubyte *dst = (VGubyte *)data;
443
444 {
445 struct pipe_transfer *transfer =
446 pipe->get_tex_transfer(pipe,
447 image->texture, 0, 0, 0,
448 PIPE_TRANSFER_READ,
449 0, 0,
450 image->x + image->width,
451 image->y + image->height);
452 /* Do a row at a time to flip image data vertically */
453 for (i = 0; i < height; i++) {
454 #if 0
455 debug_printf("%d-%d == %d\n", sy, height, y);
456 #endif
457 pipe_get_tile_rgba(pipe, transfer, sx+image->x, y, width, 1, df);
458 y += yStep;
459 _vega_pack_rgba_span_float(ctx, width, temp, dataFormat, dst);
460 dst += dataStride;
461 }
462
463 pipe->tex_transfer_destroy(pipe, transfer);
464 }
465 }
466
467 struct vg_image * image_child_image(struct vg_image *parent,
468 VGint x, VGint y,
469 VGint width, VGint height)
470 {
471 struct vg_context *ctx = vg_current_context();
472 struct vg_image *image = CALLOC_STRUCT(vg_image);
473
474 vg_init_object(&image->base, ctx, VG_OBJECT_IMAGE);
475
476 image->x = parent->x + x;
477 image->y = parent->y + y;
478 image->width = width;
479 image->height = height;
480 image->parent = parent;
481 image->texture = 0;
482 pipe_texture_reference(&image->texture,
483 parent->texture);
484
485 image->sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
486 image->sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
487 image->sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
488 image->sampler.min_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
489 image->sampler.mag_img_filter = PIPE_TEX_MIPFILTER_NEAREST;
490 image->sampler.normalized_coords = 1;
491
492 if (!parent->children_array)
493 parent->children_array = array_create(
494 sizeof(struct vg_image*));
495
496 array_append_data(parent->children_array,
497 &image, 1);
498
499 vg_context_add_object(ctx, VG_OBJECT_IMAGE, image);
500
501 return image;
502 }
503
504 void image_copy(struct vg_image *dst, VGint dx, VGint dy,
505 struct vg_image *src, VGint sx, VGint sy,
506 VGint width, VGint height,
507 VGboolean dither)
508 {
509 struct vg_context *ctx = vg_current_context();
510
511 if (width <= 0 || height <= 0) {
512 vg_set_error(ctx, VG_ILLEGAL_ARGUMENT_ERROR);
513 return;
514 }
515 /* make sure rendering has completed */
516 ctx->pipe->flush(ctx->pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
517 vg_copy_texture(ctx, dst->texture, dst->x + dx, dst->y + dy,
518 src->texture, src->x + sx, src->y + sy, width, height);
519 }
520
521 void image_draw(struct vg_image *img)
522 {
523 struct vg_context *ctx = vg_current_context();
524 VGfloat x1, y1;
525 VGfloat x2, y2;
526 VGfloat x3, y3;
527 VGfloat x4, y4;
528 struct matrix *matrix;
529
530 x1 = 0;
531 y1 = 0;
532 x2 = img->width;
533 y2 = 0;
534 x3 = img->width;
535 y3 = img->height;
536 x4 = 0;
537 y4 = img->height;
538
539 matrix = &ctx->state.vg.image_user_to_surface_matrix;
540
541 matrix_map_point(matrix, x1, y1, &x1, &y1);
542 matrix_map_point(matrix, x2, y2, &x2, &y2);
543 matrix_map_point(matrix, x3, y3, &x3, &y3);
544 matrix_map_point(matrix, x4, y4, &x4, &y4);
545
546 shader_set_drawing_image(ctx->shader, VG_TRUE);
547 shader_set_paint(ctx->shader, ctx->state.vg.fill_paint);
548 shader_set_image(ctx->shader, img);
549 shader_bind(ctx->shader);
550
551 renderer_texture_quad(ctx->renderer, image_texture(img),
552 img->x, img->y, img->x + img->width, img->y + img->height,
553 x1, y1, x2, y2, x3, y3, x4, y4);
554 }
555
556 void image_set_pixels(VGint dx, VGint dy,
557 struct vg_image *src, VGint sx, VGint sy,
558 VGint width, VGint height)
559 {
560 struct vg_context *ctx = vg_current_context();
561 struct pipe_context *pipe = ctx->pipe;
562 struct pipe_screen *screen = pipe->screen;
563 struct pipe_surface *surf;
564 struct st_renderbuffer *strb = ctx->draw_buffer->strb;
565
566 /* make sure rendering has completed */
567 pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
568
569 surf = screen->get_tex_surface(screen, image_texture(src), 0, 0, 0,
570 PIPE_BUFFER_USAGE_GPU_READ);
571
572 vg_copy_surface(ctx, strb->surface, dx, dy,
573 surf, sx+src->x, sy+src->y, width, height);
574
575 screen->tex_surface_destroy(surf);
576 }
577
578 void image_get_pixels(struct vg_image *dst, VGint dx, VGint dy,
579 VGint sx, VGint sy,
580 VGint width, VGint height)
581 {
582 struct vg_context *ctx = vg_current_context();
583 struct pipe_context *pipe = ctx->pipe;
584 struct pipe_screen *screen = pipe->screen;
585 struct pipe_surface *surf;
586 struct st_renderbuffer *strb = ctx->draw_buffer->strb;
587
588 /* flip the y coordinates */
589 /*dy = dst->height - dy - height;*/
590
591 /* make sure rendering has completed */
592 pipe->flush(pipe, PIPE_FLUSH_RENDER_CACHE, NULL);
593
594 surf = screen->get_tex_surface(screen, image_texture(dst), 0, 0, 0,
595 PIPE_BUFFER_USAGE_GPU_WRITE |
596 PIPE_BUFFER_USAGE_GPU_READ);
597 vg_copy_surface(ctx, surf, dst->x + dx, dst->y + dy,
598 strb->surface, sx, sy, width, height);
599
600 pipe_surface_reference(&surf, NULL);
601 }
602
603
604 VGboolean vg_image_overlaps(struct vg_image *dst,
605 struct vg_image *src)
606 {
607 if (dst == src || dst->parent == src ||
608 dst == src->parent)
609 return VG_TRUE;
610 if (dst->parent && dst->parent == src->parent) {
611 VGfloat left1 = dst->x;
612 VGfloat left2 = src->x;
613 VGfloat right1 = dst->x + dst->width;
614 VGfloat right2 = src->x + src->width;
615 VGfloat bottom1 = dst->y;
616 VGfloat bottom2 = src->y;
617 VGfloat top1 = dst->y + dst->height;
618 VGfloat top2 = src->y + src->height;
619
620 return !(left2 > right1 || right2 < left1 ||
621 top2 > bottom1 || bottom2 < top1);
622 }
623 return VG_FALSE;
624 }
625
626 VGint image_bind_samplers(struct vg_image *img, struct pipe_sampler_state **samplers,
627 struct pipe_texture **textures)
628 {
629 img->sampler.min_img_filter = image_sampler_filter(img->base.ctx);
630 img->sampler.mag_img_filter = image_sampler_filter(img->base.ctx);
631 samplers[3] = &img->sampler;
632 textures[3] = img->texture;
633 return 1;
634 }
635
636 VGint image_sampler_filter(struct vg_context *ctx)
637 {
638 switch(ctx->state.vg.image_quality) {
639 case VG_IMAGE_QUALITY_NONANTIALIASED:
640 return PIPE_TEX_FILTER_NEAREST;
641 break;
642 case VG_IMAGE_QUALITY_FASTER:
643 return PIPE_TEX_FILTER_NEAREST;
644 break;
645 case VG_IMAGE_QUALITY_BETTER:
646 /* possibly use anisotropic filtering */
647 return PIPE_TEX_FILTER_LINEAR;
648 break;
649 default:
650 debug_printf("Unknown image quality");
651 }
652 return PIPE_TEX_FILTER_NEAREST;
653 }