]> mj.ucw.cz Git - libucw.git/blob - images/image.c
Merge with git+ssh://cvs.ucw.cz/projects/sherlock/GIT/sherlock.git
[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 "images/error.h"
16 #include "images/color.h"
17
18 #include <string.h>
19
20 static inline uns
21 flags_to_pixel_size(uns flags)
22 {
23   uns pixel_size;
24   switch (flags & IMAGE_COLOR_SPACE)
25     {
26       case COLOR_SPACE_GRAYSCALE:
27         pixel_size = 1;
28         break;
29       case COLOR_SPACE_RGB:
30         pixel_size = 3;
31         break;
32       default:
33         ASSERT(0);
34     }
35   if (flags & IMAGE_ALPHA)
36     pixel_size++;
37   return pixel_size;
38 }
39
40 struct image *
41 image_new(struct image_context *ctx, uns cols, uns rows, uns flags, struct mempool *pool)
42 {
43   DBG("image_new(cols=%u rows=%u flags=0x%x pool=%p)", cols, rows, flags, pool);
44   flags &= IMAGE_NEW_FLAGS;
45   if (unlikely(!image_dimensions_valid(cols, rows)))
46     {
47       IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows);
48       return NULL;
49     }
50   struct image *img;
51   uns pixel_size, row_size, align;
52   pixel_size = flags_to_pixel_size(flags);
53   switch (pixel_size)
54     {
55       case 1:
56       case 2:
57       case 4:
58         flags |= IMAGE_PIXELS_ALIGNED;
59         break;
60       case 3:
61         if (flags & IMAGE_PIXELS_ALIGNED)
62           pixel_size = 4;
63         break;
64       default:
65         ASSERT(0);
66     }
67   if (flags & IMAGE_SSE_ALIGNED)
68     align = IMAGE_SSE_ALIGN_SIZE;
69   else if (flags & IMAGE_PIXELS_ALIGNED)
70     align = pixel_size;
71   else
72     align = 1;
73   row_size = cols * pixel_size;
74   row_size = ALIGN(row_size, align);
75   u64 image_size_64 = (u64)row_size * rows;
76   u64 bytes_64 = image_size_64 + (sizeof(struct image) + IMAGE_SSE_ALIGN_SIZE - 1 + sizeof(uns));
77   if (unlikely(bytes_64 > image_max_bytes))
78     {
79       IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_DIMENSIONS, "Image does not fit in memory");
80       return NULL;
81     }
82   if (pool)
83     img = mp_alloc(pool, bytes_64);
84   else
85     {
86       img = xmalloc(bytes_64);
87       flags |= IMAGE_NEED_DESTROY;
88     }
89   bzero(img, sizeof(struct image));
90   byte *p = (byte *)img + sizeof(struct image);
91   img->pixels = ALIGN_PTR(p, IMAGE_SSE_ALIGN_SIZE);
92   img->flags = flags;
93   img->pixel_size = pixel_size;
94   img->cols = cols;
95   img->rows = rows;
96   img->row_size = row_size;
97   img->image_size = image_size_64;
98   DBG("img=%p flags=0x%x pixel_size=%u row_size=%u image_size=%u pixels=%p",
99     img, img->flags, img->pixel_size, img->row_size, img->image_size, img->pixels);
100   return img;
101 }
102
103 struct image *
104 image_clone(struct image_context *ctx, struct image *src, uns flags, struct mempool *pool)
105 {
106   DBG("image_clone(src=%p flags=0x%x pool=%p)", src, src->flags, pool);
107   struct image *img;
108   flags &= IMAGE_NEW_FLAGS & ~IMAGE_CHANNELS_FORMAT;
109   flags |= src->flags & IMAGE_CHANNELS_FORMAT;
110   if (!(img = image_new(ctx, src->cols, src->rows, flags, pool)))
111     return NULL;
112   if (img->image_size)
113     {
114       if (src->pixel_size != img->pixel_size) /* conversion between aligned and unaligned RGB */
115         {
116 #         define IMAGE_WALK_PREFIX(x) walk_##x
117 #         define IMAGE_WALK_INLINE
118 #         define IMAGE_WALK_IMAGE img
119 #         define IMAGE_WALK_SEC_IMAGE src
120 #         define IMAGE_WALK_DOUBLE
121 #         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)
122 #         include "images/image-walk.h"
123         }
124       else if (src->row_size != img->row_size || ((img->flags | src->flags) & IMAGE_GAPS_PROTECTED))
125         {
126           byte *s = src->pixels;
127           byte *d = img->pixels;
128           uns bytes = src->cols * img->pixel_size;
129           for (uns row = src->rows; row--; )
130             {
131               memcpy(d, s, bytes);
132               d += img->row_size;
133               s += src->row_size;
134             }
135         }
136       else
137         memcpy(img->pixels, src->pixels, img->image_size);
138     }
139   return img;
140 }
141
142 void
143 image_destroy(struct image *img)
144 {
145   DBG("image_destroy(img=%p)", img);
146   if (img->flags & IMAGE_NEED_DESTROY)
147     xfree(img);
148 }
149
150 void
151 image_clear(struct image_context *ctx UNUSED, struct image *img)
152 {
153   DBG("image_clear(img=%p)", img);
154   if (img->image_size)
155     if (img->flags & IMAGE_GAPS_PROTECTED)
156       {
157         byte *p = img->pixels;
158         uns bytes = img->cols * img->pixel_size;
159         for (uns row = img->rows; row--; p += img->row_size)
160           bzero(p, bytes);
161       }
162     else
163       bzero(img->pixels, img->image_size);
164 }
165
166 struct image *
167 image_init_matrix(struct image_context *ctx, struct image *img, byte *pixels, uns cols, uns rows, uns row_size, uns flags)
168 {
169   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);
170   if (unlikely(!image_dimensions_valid(cols, rows)))
171     {
172       IMAGE_ERROR(ctx, IMAGE_ERROR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows);
173       return NULL;
174     }
175   img->pixels = pixels;
176   img->cols = cols;
177   img->rows = rows;
178   img->pixel_size = flags_to_pixel_size(flags);
179   img->row_size = row_size;
180   img->image_size = rows * row_size;
181   img->flags = flags & (IMAGE_NEW_FLAGS | IMAGE_GAPS_PROTECTED);
182   return img;
183 }
184
185 struct image *
186 image_init_subimage(struct image_context *ctx UNUSED, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows)
187 {
188   DBG("image_init_subimage(img=%p src=%p left=%u top=%u cols=%u rows=%u)", img, src, left, top, cols, rows);
189   ASSERT(left + cols <= src->cols && top + rows <= src->rows);
190   img->pixels = src->pixels + left * src->pixel_size + top * src->row_size;
191   img->cols = cols;
192   img->rows = rows;
193   img->pixel_size = src->pixel_size;
194   img->row_size = src->row_size;
195   img->image_size = src->row_size * rows;
196   img->flags = src->flags & IMAGE_NEW_FLAGS;
197   img->flags |= IMAGE_GAPS_PROTECTED;
198   return img;
199 }
200
201 byte *
202 color_space_to_name(uns cs)
203 {
204   return image_channels_format_to_name(cs);
205 }
206
207 byte *
208 image_channels_format_to_name(uns format)
209 {
210   switch (format)
211     {
212       case COLOR_SPACE_GRAYSCALE:
213         return "Gray";
214       case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA:
215         return "GrayAlpha";
216       case COLOR_SPACE_RGB:
217         return "RGB";
218       case COLOR_SPACE_RGB | IMAGE_ALPHA:
219         return "RGBAlpha";
220       default:
221         return NULL;
222     }
223 }
224
225 uns
226 image_name_to_channels_format(byte *name)
227 {
228   if (!strcasecmp(name, "gray"))
229     return COLOR_SPACE_GRAYSCALE;
230   if (!strcasecmp(name, "grayscale"))
231     return COLOR_SPACE_GRAYSCALE;
232   if (!strcasecmp(name, "grayalpha"))
233     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
234   if (!strcasecmp(name, "grayscalealpha"))
235     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
236   if (!strcasecmp(name, "rgb"))
237     return COLOR_SPACE_RGB;
238   if (!strcasecmp(name, "rgbalpha"))
239     return COLOR_SPACE_RGB + IMAGE_ALPHA;
240   if (!strcasecmp(name, "rgba"))
241     return COLOR_SPACE_RGB + IMAGE_ALPHA;
242   return 0;
243 }