]> mj.ucw.cz Git - libucw.git/blob - images/image.c
* dev-threads merged to dev-img
[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_pixels_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_pixels_size = cols * pixel_size;
74   row_size = ALIGN(row_pixels_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->row_pixels_size = row_pixels_size;
98   img->image_size = image_size_64;
99   DBG("img=%p flags=0x%x pixel_size=%u row_size=%u image_size=%u pixels=%p",
100     img, img->flags, img->pixel_size, img->row_size, img->image_size, img->pixels);
101   return img;
102 }
103
104 struct image *
105 image_clone(struct image_context *ctx, struct image *src, uns flags, struct mempool *pool)
106 {
107   DBG("image_clone(src=%p flags=0x%x pool=%p)", src, src->flags, pool);
108   struct image *img;
109   flags &= IMAGE_NEW_FLAGS & ~IMAGE_CHANNELS_FORMAT;
110   flags |= src->flags & IMAGE_CHANNELS_FORMAT;
111   if (!(img = image_new(ctx, src->cols, src->rows, flags, pool)))
112     return NULL;
113   if (img->image_size)
114     {
115       if (src->pixel_size != img->pixel_size) /* conversion between aligned and unaligned RGB */
116         {
117 #         define IMAGE_WALK_PREFIX(x) walk_##x
118 #         define IMAGE_WALK_INLINE
119 #         define IMAGE_WALK_IMAGE img
120 #         define IMAGE_WALK_SEC_IMAGE src
121 #         define IMAGE_WALK_DOUBLE
122 #         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)
123 #         include "images/image-walk.h"
124         }
125       else if (src->row_size != img->row_size || ((img->flags | src->flags) & IMAGE_GAPS_PROTECTED))
126         {
127           byte *s = src->pixels;
128           byte *d = img->pixels;
129           for (uns row = src->rows; row--; )
130             {
131               memcpy(d, s, src->row_pixels_size);
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->row_pixels_size = cols * img->pixel_size;
181   img->image_size = rows * row_size;
182   img->flags = flags & (IMAGE_NEW_FLAGS | IMAGE_GAPS_PROTECTED);
183   return img;
184 }
185
186 struct image *
187 image_init_subimage(struct image_context *ctx UNUSED, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows)
188 {
189   DBG("image_init_subimage(img=%p src=%p left=%u top=%u cols=%u rows=%u)", img, src, left, top, cols, rows);
190   ASSERT(left + cols <= src->cols && top + rows <= src->rows);
191   img->pixels = src->pixels + left * src->pixel_size + top * src->row_size;
192   img->cols = cols;
193   img->rows = rows;
194   img->pixel_size = src->pixel_size;
195   img->row_size = src->row_size;
196   img->row_pixels_size = cols * src->pixel_size;
197   img->image_size = src->row_size * rows;
198   img->flags = src->flags & IMAGE_NEW_FLAGS;
199   img->flags |= IMAGE_GAPS_PROTECTED;
200   return img;
201 }
202
203 byte *
204 color_space_to_name(uns cs)
205 {
206   return image_channels_format_to_name(cs);
207 }
208
209 byte *
210 image_channels_format_to_name(uns format)
211 {
212   switch (format)
213     {
214       case COLOR_SPACE_GRAYSCALE:
215         return "Gray";
216       case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA:
217         return "GrayAlpha";
218       case COLOR_SPACE_RGB:
219         return "RGB";
220       case COLOR_SPACE_RGB | IMAGE_ALPHA:
221         return "RGBAlpha";
222       default:
223         return NULL;
224     }
225 }
226
227 uns
228 image_name_to_channels_format(byte *name)
229 {
230   if (!strcasecmp(name, "gray"))
231     return COLOR_SPACE_GRAYSCALE;
232   if (!strcasecmp(name, "grayscale"))
233     return COLOR_SPACE_GRAYSCALE;
234   if (!strcasecmp(name, "grayalpha"))
235     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
236   if (!strcasecmp(name, "grayscalealpha"))
237     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
238   if (!strcasecmp(name, "rgb"))
239     return COLOR_SPACE_RGB;
240   if (!strcasecmp(name, "rgbalpha"))
241     return COLOR_SPACE_RGB | IMAGE_ALPHA;
242   if (!strcasecmp(name, "rgba"))
243     return COLOR_SPACE_RGB | IMAGE_ALPHA;
244   return 0;
245 }