]> mj.ucw.cz Git - libucw.git/blob - images/image.c
deb8f9f81141f81b4d53425ad2e6074c5a9b8b7c
[libucw.git] / images / image.c
1 /*
2  *      Image Library -- Basic image manipulation
3  *
4  *      (c) 2006 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #undef LOCAL_DEBUG
11
12 #include "lib/lib.h"
13 #include "lib/mempool.h"
14 #include "images/images.h"
15 #include <string.h>
16
17 #define MAX_IMAGE_BYTES (1 << 30)
18
19 void
20 image_thread_init(struct image_thread *it)
21 {
22   DBG("image_thread_init()");
23   bzero(it, sizeof(*it));
24   it->pool = mp_new(1024);
25 }
26
27 void
28 image_thread_cleanup(struct image_thread *it)
29 {
30   DBG("image_thread_cleanup()");
31   mp_delete(it->pool);
32 }
33
34 void
35 image_thread_err_format(struct image_thread *it, uns code, char *msg, ...)
36 {
37   va_list args;
38   va_start(args, msg);
39   it->err_code = code;
40   it->err_msg = mp_vprintf(it->pool, msg, args);
41   va_end(args);
42 }
43
44 static inline uns
45 flags_to_pixel_size(uns flags)
46 {
47   uns pixel_size;
48   switch (flags & IMAGE_COLOR_SPACE)
49     {
50       case COLOR_SPACE_GRAYSCALE:
51         pixel_size = 1;
52         break;
53       case COLOR_SPACE_RGB:
54         pixel_size = 3;
55         break;
56       default:
57         ASSERT(0);
58     }
59   if (flags & IMAGE_ALPHA)
60     pixel_size++;
61   return pixel_size;
62 }
63
64 struct image *
65 image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool *pool)
66 {
67   DBG("image_new(cols=%u rows=%u flags=0x%x pool=%p)", cols, rows, flags, pool);
68   flags &= IMAGE_NEW_FLAGS;
69   if (unlikely(!image_dimensions_valid(cols, rows)))
70     {
71       image_thread_err_format(it, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows);
72       return NULL;
73     }
74   struct image *img;
75   uns pixel_size, row_size, align;
76   pixel_size = flags_to_pixel_size(flags);
77   switch (pixel_size)
78     {
79       case 1:
80       case 2:
81       case 4:
82         flags |= IMAGE_PIXELS_ALIGNED;
83         break;
84       case 3:
85         if (flags & IMAGE_PIXELS_ALIGNED)
86           pixel_size = 4;
87         break;
88       default:
89         ASSERT(0);
90     }
91   if (flags & IMAGE_SSE_ALIGNED)
92     align = IMAGE_SSE_ALIGN_SIZE;
93   else if (flags & IMAGE_PIXELS_ALIGNED)
94     align = pixel_size;
95   else
96     align = 1;
97   row_size = cols * pixel_size;
98   row_size = ALIGN(row_size, align);
99   u64 image_size_64 = (u64)row_size * rows;
100   u64 bytes_64 = image_size_64 + (sizeof(struct image) + IMAGE_SSE_ALIGN_SIZE - 1 + sizeof(uns));
101   if (unlikely(bytes_64 > MAX_IMAGE_BYTES))
102     {
103       image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image does not fit in memory");
104       return NULL;
105     }
106   if (pool)
107     img = mp_alloc(pool, bytes_64);
108   else
109     {
110       img = xmalloc(bytes_64);
111       flags |= IMAGE_NEED_DESTROY;
112     }
113   bzero(img, sizeof(struct image));
114   byte *p = (byte *)img + sizeof(struct image);
115   img->pixels = ALIGN_PTR(p, IMAGE_SSE_ALIGN_SIZE);
116   img->flags = flags;
117   img->pixel_size = pixel_size;
118   img->cols = cols;
119   img->rows = rows;
120   img->row_size = row_size;
121   img->image_size = image_size_64;
122   DBG("img=%p flags=0x%x pixel_size=%u row_size=%u image_size=%u pixels=%p",
123     img, img->flags, img->pixel_size, img->row_size, img->image_size, img->pixels);
124   return img;
125 }
126
127 struct image *
128 image_clone(struct image_thread *it, struct image *src, uns flags, struct mempool *pool)
129 {
130   DBG("image_clone(src=%p flags=0x%x pool=%p)", src, src->flags, pool);
131   struct image *img;
132   flags &= IMAGE_NEW_FLAGS & ~IMAGE_CHANNELS_FORMAT;
133   flags |= src->flags & IMAGE_CHANNELS_FORMAT;
134   if (!(img = image_new(it, src->cols, src->rows, flags, pool)))
135     return NULL;
136   if (img->image_size)
137     {
138       if (src->pixel_size != img->pixel_size) /* conversion between aligned and unaligned RGB */
139         {
140 #         define IMAGE_WALK_PREFIX(x) walk_##x
141 #         define IMAGE_WALK_INLINE
142 #         define IMAGE_WALK_IMAGE img
143 #         define IMAGE_WALK_SEC_IMAGE src
144 #         define IMAGE_WALK_DOUBLE
145 #         define IMAGE_WALK_DO_STEP do{ walk_pos[0] = walk_sec_pos[0]; walk_pos[1] = walk_sec_pos[1]; walk_pos[2] = walk_sec_pos[2]; }while(0)
146 #         include "images/image-walk.h"
147         }
148       else if (src->row_size != img->row_size || ((img->flags | src->flags) & IMAGE_GAPS_PROTECTED))
149         {
150           byte *s = src->pixels;
151           byte *d = img->pixels;
152           uns bytes = src->cols * img->pixel_size;
153           for (uns row = src->rows; row--; )
154             {
155               memcpy(d, s, bytes);
156               d += img->row_size;
157               s += src->row_size;
158             }
159         }
160       else
161         memcpy(img->pixels, src->pixels, img->image_size);
162     }
163   return img;
164 }
165
166 void
167 image_destroy(struct image *img)
168 {
169   DBG("image_destroy(img=%p)", img);
170   if (img->flags & IMAGE_NEED_DESTROY)
171     xfree(img);
172 }
173
174 void
175 image_clear(struct image_thread *it UNUSED, struct image *img)
176 {
177   DBG("image_clear(img=%p)", img);
178   if (img->image_size)
179     if (img->flags & IMAGE_GAPS_PROTECTED)
180       {
181         byte *p = img->pixels;
182         uns bytes = img->cols * img->pixel_size;
183         for (uns row = img->rows; row--; p += img->row_size)
184           bzero(p, bytes);
185       }
186     else
187       bzero(img->pixels, img->image_size);
188 }
189
190 struct image *
191 image_init_matrix(struct image_thread *it, struct image *img, byte *pixels, uns cols, uns rows, uns row_size, uns flags)
192 {
193   DBG("image_init_matrix(img=%p pixels=%p cols=%u rows=%u row_size=%u flags=0x%x)", img, pixels, cols, rows, row_size, flags);
194   if (unlikely(!image_dimensions_valid(cols, rows)))
195     {
196       image_thread_err_format(it, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows);
197       return NULL;
198     }
199   img->pixels = pixels;
200   img->cols = cols;
201   img->rows = rows;
202   img->pixel_size = flags_to_pixel_size(flags);
203   img->row_size = row_size;
204   img->image_size = rows * row_size;
205   img->flags = flags & (IMAGE_NEW_FLAGS | IMAGE_GAPS_PROTECTED);
206   return img;
207 }
208
209 struct image *
210 image_init_subimage(struct image_thread *it UNUSED, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows)
211 {
212   DBG("image_init_subimage(img=%p src=%p left=%u top=%u cols=%u rows=%u)", img, src, left, top, cols, rows);
213   ASSERT(left + cols <= src->cols && top + rows <= src->rows);
214   img->pixels = src->pixels + left * src->pixel_size + top * src->row_size;
215   img->cols = cols;
216   img->rows = rows;
217   img->pixel_size = src->pixel_size;
218   img->row_size = src->row_size;
219   img->image_size = src->row_size * rows;
220   img->flags = src->flags & IMAGE_NEW_FLAGS;
221   img->flags |= IMAGE_GAPS_PROTECTED;
222   return img;
223 }
224
225 byte *
226 color_space_to_name(enum color_space cs)
227 {
228   return image_channels_format_to_name(cs);
229 }
230
231 byte *
232 image_channels_format_to_name(uns format)
233 {
234   switch (format)
235     {
236       case COLOR_SPACE_GRAYSCALE:
237         return "Gray";
238       case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA:
239         return "GrayAlpha";
240       case COLOR_SPACE_RGB:
241         return "RGB";
242       case COLOR_SPACE_RGB | IMAGE_ALPHA:
243         return "RGBAlpha";
244       default:
245         return NULL;
246     }
247 }
248
249 uns
250 image_name_to_channels_format(byte *name)
251 {
252   if (!strcasecmp(name, "gray"))
253     return COLOR_SPACE_GRAYSCALE;
254   if (!strcasecmp(name, "grayscale"))
255     return COLOR_SPACE_GRAYSCALE;
256   if (!strcasecmp(name, "grayalpha"))
257     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
258   if (!strcasecmp(name, "grayscalealpha"))
259     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
260   if (!strcasecmp(name, "rgb"))
261     return COLOR_SPACE_RGB;
262   if (!strcasecmp(name, "rgbalpha"))
263     return COLOR_SPACE_RGB + IMAGE_ALPHA;
264   if (!strcasecmp(name, "rgba"))
265     return COLOR_SPACE_RGB + IMAGE_ALPHA;
266   return 0;
267 }