"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / cogl-bitmap.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2007,2008,2009 Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "cogl-util.h"
29 #include "cogl-debug.h"
30 #include "cogl-private.h"
31 #include "cogl-bitmap-private.h"
32 #include "cogl-buffer-private.h"
33 #include "cogl-pixel-buffer.h"
34 #include "cogl-context-private.h"
35
36 #include <string.h>
37
38 struct _CoglBitmap
39 {
40   CoglHandleObject         _parent;
41   CoglPixelFormat          format;
42   int                      width;
43   int                      height;
44   int                      rowstride;
45
46   guint8                  *data;
47
48   gboolean                 mapped;
49   gboolean                 bound;
50
51   /* If this is non-null then 'data' is ignored and instead it is
52      fetched from this shared bitmap. */
53   CoglBitmap              *shared_bmp;
54
55   /* If this is non-null then 'data' is treated as an offset into the
56      buffer and map will divert to mapping the buffer */
57   CoglBuffer              *buffer;
58 };
59
60 static void _cogl_bitmap_free (CoglBitmap *bmp);
61
62 COGL_OBJECT_DEFINE (Bitmap, bitmap);
63
64 static void
65 _cogl_bitmap_free (CoglBitmap *bmp)
66 {
67   g_assert (!bmp->mapped);
68   g_assert (!bmp->bound);
69
70   if (bmp->shared_bmp)
71     cogl_object_unref (bmp->shared_bmp);
72
73   if (bmp->buffer)
74     cogl_object_unref (bmp->buffer);
75
76   g_slice_free (CoglBitmap, bmp);
77 }
78
79 gboolean
80 _cogl_bitmap_convert_premult_status (CoglBitmap      *bmp,
81                                      CoglPixelFormat  dst_format)
82 {
83   /* Do we need to unpremultiply? */
84   if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
85       (dst_format & COGL_PREMULT_BIT) == 0 &&
86       COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (dst_format))
87     return _cogl_bitmap_unpremult (bmp);
88
89   /* Do we need to premultiply? */
90   if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
91       COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (bmp->format) &&
92       (dst_format & COGL_PREMULT_BIT) > 0)
93     /* Try premultiplying using imaging library */
94     return _cogl_bitmap_premult (bmp);
95
96   return TRUE;
97 }
98
99 CoglBitmap *
100 _cogl_bitmap_copy (CoglBitmap *src_bmp)
101 {
102   CoglBitmap *dst_bmp;
103   CoglPixelFormat src_format = cogl_bitmap_get_format (src_bmp);
104   int width = cogl_bitmap_get_width (src_bmp);
105   int height = cogl_bitmap_get_height (src_bmp);
106
107   _COGL_GET_CONTEXT (ctx, NULL);
108
109   dst_bmp =
110     _cogl_bitmap_new_with_malloc_buffer (ctx,
111                                          width, height,
112                                          src_format);
113
114   _cogl_bitmap_copy_subregion (src_bmp,
115                                dst_bmp,
116                                0, 0, /* src_x/y */
117                                0, 0, /* dst_x/y */
118                                width, height);
119
120   return dst_bmp;
121 }
122
123 gboolean
124 _cogl_bitmap_copy_subregion (CoglBitmap *src,
125                              CoglBitmap *dst,
126                              int         src_x,
127                              int         src_y,
128                              int         dst_x,
129                              int         dst_y,
130                              int         width,
131                              int         height)
132 {
133   guint8 *srcdata;
134   guint8 *dstdata;
135   int    bpp;
136   int    line;
137   gboolean succeeded = FALSE;
138
139   /* Intended only for fast copies when format is equal! */
140   g_assert ((src->format & ~COGL_PREMULT_BIT) ==
141             (dst->format & ~COGL_PREMULT_BIT));
142
143   bpp = _cogl_pixel_format_get_bytes_per_pixel (src->format);
144
145   if ((srcdata = _cogl_bitmap_map (src, COGL_BUFFER_ACCESS_READ, 0)))
146     {
147       if ((dstdata = _cogl_bitmap_map (dst, COGL_BUFFER_ACCESS_WRITE, 0)))
148         {
149           srcdata += src_y * src->rowstride + src_x * bpp;
150           dstdata += dst_y * dst->rowstride + dst_x * bpp;
151
152           for (line=0; line<height; ++line)
153             {
154               memcpy (dstdata, srcdata, width * bpp);
155               srcdata += src->rowstride;
156               dstdata += dst->rowstride;
157             }
158
159           succeeded = TRUE;
160
161           _cogl_bitmap_unmap (dst);
162         }
163
164       _cogl_bitmap_unmap (src);
165     }
166
167   return succeeded;
168 }
169
170 gboolean
171 cogl_bitmap_get_size_from_file (const char *filename,
172                                 int        *width,
173                                 int        *height)
174 {
175   return _cogl_bitmap_get_size_from_file (filename, width, height);
176 }
177
178 CoglBitmap *
179 cogl_bitmap_new_for_data (CoglContext *context,
180                           int width,
181                           int height,
182                           CoglPixelFormat format,
183                           int rowstride,
184                           guint8 *data)
185 {
186   CoglBitmap *bmp;
187
188   g_return_val_if_fail (cogl_is_context (context), NULL);
189
190   bmp = g_slice_new (CoglBitmap);
191   bmp->format = format;
192   bmp->width = width;
193   bmp->height = height;
194   bmp->rowstride = rowstride;
195   bmp->data = data;
196   bmp->mapped = FALSE;
197   bmp->bound = FALSE;
198   bmp->shared_bmp = NULL;
199   bmp->buffer = NULL;
200
201   return _cogl_bitmap_object_new (bmp);
202 }
203
204 CoglBitmap *
205 _cogl_bitmap_new_with_malloc_buffer (CoglContext *context,
206                                      unsigned int width,
207                                      unsigned int height,
208                                      CoglPixelFormat format)
209 {
210   static CoglUserDataKey bitmap_free_key;
211   int bpp = _cogl_pixel_format_get_bytes_per_pixel (format);
212   int rowstride = ((width * bpp) + 3) & ~3;
213   guint8 *data = g_malloc (rowstride * height);
214   CoglBitmap *bitmap;
215
216   bitmap = cogl_bitmap_new_for_data (context,
217                                      width, height,
218                                      format,
219                                      rowstride,
220                                      data);
221   cogl_object_set_user_data (COGL_OBJECT (bitmap),
222                              &bitmap_free_key,
223                              data,
224                              g_free);
225
226   return bitmap;
227 }
228
229 CoglBitmap *
230 _cogl_bitmap_new_shared (CoglBitmap              *shared_bmp,
231                          CoglPixelFormat          format,
232                          int                      width,
233                          int                      height,
234                          int                      rowstride)
235 {
236   CoglBitmap *bmp;
237
238   _COGL_GET_CONTEXT (ctx, NULL);
239
240   bmp = cogl_bitmap_new_for_data (ctx,
241                                   width, height,
242                                   format,
243                                   rowstride,
244                                   NULL /* data */);
245
246   bmp->shared_bmp = cogl_object_ref (shared_bmp);
247
248   return bmp;
249 }
250
251 CoglBitmap *
252 cogl_bitmap_new_from_file (const char  *filename,
253                            GError     **error)
254 {
255   _COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, COGL_INVALID_HANDLE);
256
257   return _cogl_bitmap_from_file (filename, error);
258 }
259
260 CoglBitmap *
261 cogl_bitmap_new_from_buffer (CoglBuffer *buffer,
262                              CoglPixelFormat format,
263                              int width,
264                              int height,
265                              int rowstride,
266                              int offset)
267 {
268   CoglBitmap *bmp;
269
270   _COGL_RETURN_VAL_IF_FAIL (cogl_is_buffer (buffer), NULL);
271
272   bmp = cogl_bitmap_new_for_data (buffer->context,
273                                   width, height,
274                                   format,
275                                   rowstride,
276                                   NULL /* data */);
277
278   bmp->buffer = cogl_object_ref (buffer);
279   bmp->data = GINT_TO_POINTER (offset);
280
281   return bmp;
282 }
283
284 CoglBitmap *
285 cogl_bitmap_new_with_size (CoglContext *context,
286                            unsigned int width,
287                            unsigned int height,
288                            CoglPixelFormat format)
289 {
290   CoglPixelBuffer *pixel_buffer;
291   CoglBitmap *bitmap;
292   unsigned int rowstride;
293
294   /* creating a buffer to store "any" format does not make sense */
295   if (G_UNLIKELY (format == COGL_PIXEL_FORMAT_ANY))
296     return NULL;
297
298   /* for now we fallback to cogl_pixel_buffer_new, later, we could ask
299    * libdrm a tiled buffer for instance */
300   rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format);
301
302   pixel_buffer = cogl_pixel_buffer_new (context, height * rowstride, NULL);
303
304   if (G_UNLIKELY (pixel_buffer == NULL))
305     return NULL;
306
307   bitmap = cogl_bitmap_new_from_buffer (COGL_BUFFER (pixel_buffer),
308                                         format,
309                                         width, height,
310                                         rowstride,
311                                         0 /* offset */);
312
313   cogl_object_unref (pixel_buffer);
314
315   return bitmap;
316 }
317
318 CoglPixelFormat
319 cogl_bitmap_get_format (CoglBitmap *bitmap)
320 {
321   return bitmap->format;
322 }
323
324 void
325 _cogl_bitmap_set_format (CoglBitmap *bitmap,
326                          CoglPixelFormat format)
327 {
328   bitmap->format = format;
329 }
330
331 int
332 cogl_bitmap_get_width (CoglBitmap *bitmap)
333 {
334   return bitmap->width;
335 }
336
337 int
338 cogl_bitmap_get_height (CoglBitmap *bitmap)
339 {
340   return bitmap->height;
341 }
342
343 int
344 cogl_bitmap_get_rowstride (CoglBitmap *bitmap)
345 {
346   return bitmap->rowstride;
347 }
348
349 CoglPixelBuffer *
350 cogl_bitmap_get_buffer (CoglBitmap *bitmap)
351 {
352   while (bitmap->shared_bmp)
353     bitmap = bitmap->shared_bmp;
354
355   return COGL_PIXEL_BUFFER (bitmap->buffer);
356 }
357
358 GQuark
359 cogl_bitmap_error_quark (void)
360 {
361   return g_quark_from_static_string ("cogl-bitmap-error-quark");
362 }
363
364 guint8 *
365 _cogl_bitmap_map (CoglBitmap *bitmap,
366                   CoglBufferAccess access,
367                   CoglBufferMapHint hints)
368 {
369   /* Divert to another bitmap if this data is shared */
370   if (bitmap->shared_bmp)
371     return _cogl_bitmap_map (bitmap->shared_bmp, access, hints);
372
373   g_assert (!bitmap->mapped);
374
375   if (bitmap->buffer)
376     {
377       guint8 *data = cogl_buffer_map (bitmap->buffer,
378                                       access,
379                                       hints);
380
381       COGL_NOTE (BITMAP, "A pixel array is being mapped from a bitmap. This "
382                  "usually means that some conversion on the pixel array is "
383                  "needed so a sub-optimal format is being used.");
384
385       if (data)
386         {
387           bitmap->mapped = TRUE;
388
389           return data + GPOINTER_TO_INT (bitmap->data);
390         }
391       else
392         return NULL;
393     }
394   else
395     {
396       bitmap->mapped = TRUE;
397
398       return bitmap->data;
399     }
400 }
401
402 void
403 _cogl_bitmap_unmap (CoglBitmap *bitmap)
404 {
405   /* Divert to another bitmap if this data is shared */
406   if (bitmap->shared_bmp)
407     {
408       _cogl_bitmap_unmap (bitmap->shared_bmp);
409       return;
410     }
411
412   g_assert (bitmap->mapped);
413   bitmap->mapped = FALSE;
414
415   if (bitmap->buffer)
416     cogl_buffer_unmap (bitmap->buffer);
417 }
418
419 guint8 *
420 _cogl_bitmap_bind (CoglBitmap *bitmap,
421                    CoglBufferAccess access,
422                    CoglBufferMapHint hints)
423 {
424   guint8 *ptr;
425
426   /* Divert to another bitmap if this data is shared */
427   if (bitmap->shared_bmp)
428     return _cogl_bitmap_bind (bitmap->shared_bmp, access, hints);
429
430   g_assert (!bitmap->bound);
431
432   /* If the bitmap wasn't created from a buffer then the
433      implementation of bind is the same as map */
434   if (bitmap->buffer == NULL)
435     {
436       guint8 *data = _cogl_bitmap_map (bitmap, access, hints);
437       if (data)
438         bitmap->bound = TRUE;
439       return data;
440     }
441
442   bitmap->bound = TRUE;
443
444   if (access == COGL_BUFFER_ACCESS_READ)
445     ptr = _cogl_buffer_bind (bitmap->buffer,
446                              COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK);
447   else if (access == COGL_BUFFER_ACCESS_WRITE)
448     ptr = _cogl_buffer_bind (bitmap->buffer,
449                              COGL_BUFFER_BIND_TARGET_PIXEL_PACK);
450   else
451     g_assert_not_reached ();
452
453   /* The data pointer actually stores the offset */
454   return GPOINTER_TO_INT (bitmap->data) + ptr;
455 }
456
457 void
458 _cogl_bitmap_unbind (CoglBitmap *bitmap)
459 {
460   /* Divert to another bitmap if this data is shared */
461   if (bitmap->shared_bmp)
462     {
463       _cogl_bitmap_unbind (bitmap->shared_bmp);
464       return;
465     }
466
467   g_assert (bitmap->bound);
468   bitmap->bound = FALSE;
469
470   /* If the bitmap wasn't created from a pixel array then the
471      implementation of unbind is the same as unmap */
472   if (bitmap->buffer)
473     _cogl_buffer_unbind (bitmap->buffer);
474   else
475     _cogl_bitmap_unmap (bitmap);
476 }