1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2009 Red Hat, Inc.
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <config.h>
19 
20 #include "canvas_utils.h"
21 #include "mem.h"
22 
23 typedef struct PixmanData {
24     uint8_t *data;
25     pixman_format_code_t format;
26 } PixmanData;
27 
release_data(SPICE_GNUC_UNUSED pixman_image_t * image,void * release_data)28 static void release_data(SPICE_GNUC_UNUSED pixman_image_t *image,
29                          void *release_data)
30 {
31     PixmanData *data = (PixmanData *)release_data;
32 
33     free(data->data);
34 
35     free(data);
36 }
37 
38 static PixmanData *
pixman_image_add_data(pixman_image_t * image)39 pixman_image_add_data(pixman_image_t *image)
40 {
41     PixmanData *data;
42 
43     data = (PixmanData *)pixman_image_get_destroy_data(image);
44     if (data == NULL) {
45         data = (PixmanData *)calloc(1, sizeof(PixmanData));
46         if (data == NULL) {
47             spice_error("out of memory");
48         }
49         pixman_image_set_destroy_function(image,
50                                           release_data,
51                                           data);
52     }
53 
54     return data;
55 }
56 
57 void
spice_pixman_image_set_format(pixman_image_t * image,pixman_format_code_t format)58 spice_pixman_image_set_format(pixman_image_t *image,
59                               pixman_format_code_t format)
60 {
61     PixmanData *data;
62 
63     data = pixman_image_add_data(image);
64     data->format = format;
65 }
66 
67 
spice_pixman_image_get_format(pixman_image_t * image,pixman_format_code_t * format)68 int spice_pixman_image_get_format(pixman_image_t *image, pixman_format_code_t *format)
69 {
70     PixmanData *data;
71 
72     spice_return_val_if_fail(format != NULL, 0);
73 
74     data = (PixmanData *)pixman_image_get_destroy_data(image);
75     if (data != NULL && data->format != 0) {
76         *format = data->format;
77         return 1;
78     }
79 
80     spice_warn_if_reached();
81     return 0;
82 }
83 
surface_create_stride(pixman_format_code_t format,int width,int height,int stride)84 pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
85                                       int stride)
86 {
87     uint8_t *data;
88     uint8_t *stride_data;
89     pixman_image_t *surface;
90     PixmanData *pixman_data;
91 
92     data = (uint8_t *)spice_malloc_n(abs(stride), height);
93     if (stride < 0) {
94         stride_data = data + (-stride) * (height - 1);
95     } else {
96         stride_data = data;
97     }
98 
99     surface = pixman_image_create_bits(format, width, height, (uint32_t *)stride_data, stride);
100 
101     if (surface == NULL) {
102         free(data);
103         data = NULL;
104         spice_error("create surface failed, out of memory");
105     }
106 
107     pixman_data = pixman_image_add_data(surface);
108     pixman_data->data = data;
109     pixman_data->format = format;
110 
111     return surface;
112 }
113 
surface_create(pixman_format_code_t format,int width,int height,int top_down)114 pixman_image_t * surface_create(pixman_format_code_t format, int width, int height, int top_down)
115 {
116     if (top_down) {
117         pixman_image_t *surface;
118         PixmanData *data;
119 
120         surface = pixman_image_create_bits(format, width, height, NULL, 0);
121         data = pixman_image_add_data(surface);
122         data->format = format;
123         return surface;
124     } else {
125         // NOTE: we assume here that the lz decoders always decode to RGB32.
126         int stride = 0;
127         switch (format) {
128         case PIXMAN_a8r8g8b8:
129         case PIXMAN_x8r8g8b8:
130 #ifdef WORDS_BIGENDIAN
131         case PIXMAN_b8g8r8a8:
132         case PIXMAN_b8g8r8x8:
133 #endif
134             stride = width * 4;
135             break;
136         case PIXMAN_r8g8b8:
137 #ifdef WORDS_BIGENDIAN
138         case PIXMAN_b8g8r8:
139 #endif
140             // NOTE: LZ4 also decodes to RGB24
141             stride = SPICE_ALIGN(width * 3, 4);
142             break;
143         case PIXMAN_x1r5g5b5:
144         case PIXMAN_r5g6b5:
145             stride = SPICE_ALIGN(width * 2, 4);
146             break;
147         case PIXMAN_a8:
148             stride = SPICE_ALIGN(width, 4);
149             break;
150         case PIXMAN_a1:
151             stride = SPICE_ALIGN(width, 32) / 8;
152             break;
153         default:
154             spice_error("invalid format");
155         }
156         stride = -stride;
157         return surface_create_stride(format, width, height, stride);
158     }
159 }
160 
alloc_lz_image_surface(LzDecodeUsrData * canvas_data,pixman_format_code_t pixman_format,int width,int height,int gross_pixels,int top_down)161 pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data,
162                                        pixman_format_code_t pixman_format, int width,
163                                        int height, int gross_pixels, int top_down)
164 {
165     int stride;
166     pixman_image_t *surface = NULL;
167 
168     stride = (gross_pixels / height) * (PIXMAN_FORMAT_BPP (pixman_format) / 8);
169 
170     /* pixman requires strides to be 4-byte aligned */
171     stride = SPICE_ALIGN(stride, 4);
172 
173     if (!top_down) {
174         stride = -stride;
175     }
176 
177     surface = surface_create_stride(pixman_format, width, height, stride);
178     canvas_data->out_surface = surface;
179     return surface;
180 }
181