]> mj.ucw.cz Git - libucw.git/blob - images/image.c
378b89220ab23ed3ba22954bcecf5d0a5263de13
[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 struct image *
45 image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool *pool)
46 {
47   DBG("image_new(cols=%u rows=%u flags=0x%x pool=%p)", cols, rows, flags, pool);
48   if (cols > IMAGE_MAX_SIZE || rows > IMAGE_MAX_SIZE)
49     {
50       image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image dimension(s) too large");
51       return NULL;
52     }
53   struct image *img;
54   uns pixel_size, row_size, image_size, align;
55   switch (flags & IMAGE_COLOR_SPACE)
56     {
57       case COLOR_SPACE_GRAYSCALE:
58         pixel_size = 1;
59         break;
60       case COLOR_SPACE_RGB:
61         pixel_size = 3;
62         break;
63       default:
64         ASSERT(0);
65     }
66   if (flags & IMAGE_ALPHA)
67     pixel_size++;
68   switch (pixel_size)
69     {
70       case 1:
71       case 2:
72       case 4:
73         flags |= IMAGE_PIXELS_ALIGNED;
74         break;
75       case 3:
76         if (flags & IMAGE_PIXELS_ALIGNED)
77           pixel_size = 4;
78         break;
79       default:
80         ASSERT(0);
81     }
82   if (flags & IMAGE_SSE_ALIGNED)
83     align = IMAGE_SSE_ALIGN_SIZE;
84   else if (flags & IMAGE_PIXELS_ALIGNED)
85     align = pixel_size;
86   else
87     align = 1;
88   row_size = cols * pixel_size;
89   row_size = ALIGN(row_size, align);
90   u64 image_size_64 = (u64)row_size * rows;
91   u64 bytes_64 = image_size_64 + (sizeof(struct image) + IMAGE_SSE_ALIGN_SIZE - 1 + sizeof(uns));
92   if (bytes_64 > MAX_IMAGE_BYTES)
93     {
94       image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image does not fit in memory");
95       return NULL;
96     }
97   if (!(image_size = image_size_64))
98     {
99       image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Zero dimension(s)");
100       return NULL;
101     }
102   img = pool ? mp_alloc(pool, (uns)bytes_64) : xmalloc((uns)bytes_64);
103   bzero(img, sizeof(struct image));
104   byte *p = (byte *)img + sizeof(struct image);
105   img->pixels = ALIGN_PTR(p, IMAGE_SSE_ALIGN_SIZE);
106   img->flags = flags;
107   img->pixel_size = pixel_size;
108   img->cols = cols;
109   img->rows = rows;
110   img->row_size = row_size;
111   img->image_size = image_size;
112   DBG("img=%p flags=0x%x pixel_size=%u row_size=%u image_size=%u pixels=%p",
113     img, img->flags, img->pixel_size, img->row_size, img->image_size, img->pixels);
114   return img;
115 }
116
117 struct image *
118 image_clone(struct image_thread *it, struct image *src, uns flags, struct mempool *pool)
119 {
120   DBG("image_clone(src=%p flags=0x%x pool=%p)", src, src->flags, pool);
121   struct image *img;
122   flags &= ~IMAGE_CHANNELS_FORMAT;
123   flags |= src->flags & IMAGE_CHANNELS_FORMAT;
124   if (!(img = image_new(it, src->cols, src->rows, flags, pool)))
125     return NULL;
126   if (img->image_size)
127     {
128       if (src->pixel_size != img->pixel_size) /* conversion between aligned and unaligned RGB */
129         {
130 #         define IMAGE_WALK_PREFIX(x) walk_##x
131 #         define IMAGE_WALK_INLINE
132 #         define IMAGE_WALK_IMAGE img
133 #         define IMAGE_WALK_SEC_IMAGE src
134 #         define IMAGE_WALK_DOUBLE
135 #         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)
136 #         include "images/image-walk.h"
137         }
138       else if (src->row_size != img->row_size)
139         {
140           byte *s = src->pixels;
141           byte *d = img->pixels;
142           uns bytes = src->cols * img->pixel_size;
143           for (uns row = src->rows; row--; )
144             {
145               memcpy(d, s, bytes);
146               d += img->row_size;
147               s += src->row_size;
148             }
149         }
150       else
151         memcpy(img->pixels, src->pixels, img->image_size);
152     }
153   return img;
154 }
155
156 void
157 image_destroy(struct image *img)
158 {
159   DBG("image_destroy(img=%p)", img);
160   xfree((byte *)img);
161 }
162
163 void
164 image_clear(struct image_thread *it UNUSED, struct image *img)
165 {
166   DBG("image_clear(img=%p)", img);
167   if (img->image_size)
168     bzero(img->pixels, img->image_size);
169 }
170
171 byte *
172 color_space_to_name(enum color_space cs)
173 {
174   return image_channels_format_to_name(cs);
175 }
176
177 byte *
178 image_channels_format_to_name(uns format)
179 {
180   switch (format)
181     {
182       case COLOR_SPACE_GRAYSCALE:
183         return "Gray";
184       case COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA:
185         return "GrayAlpha";
186       case COLOR_SPACE_RGB:
187         return "RGB";
188       case COLOR_SPACE_RGB | IMAGE_ALPHA:
189         return "RGBAlpha";
190       default:
191         return NULL;
192     }
193 }
194
195 uns
196 image_name_to_channels_format(byte *name)
197 {
198   if (!strcasecmp(name, "gray"))
199     return COLOR_SPACE_GRAYSCALE;
200   if (!strcasecmp(name, "grayscale"))
201     return COLOR_SPACE_GRAYSCALE;
202   if (!strcasecmp(name, "grayalpha"))
203     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
204   if (!strcasecmp(name, "grayscalealpha"))
205     return COLOR_SPACE_GRAYSCALE | IMAGE_ALPHA;
206   if (!strcasecmp(name, "rgb"))
207     return COLOR_SPACE_RGB;
208   if (!strcasecmp(name, "rgbalpha"))
209     return COLOR_SPACE_RGB + IMAGE_ALPHA;
210   if (!strcasecmp(name, "rgba"))
211     return COLOR_SPACE_RGB + IMAGE_ALPHA;
212   return 0;
213 }