"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl-pango / cogl-pango-pipeline-cache.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2011 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
20  * <http://www.gnu.org/licenses/>.
21  *
22  *
23  *
24  * Authors:
25  *   Neil Roberts <neil@linux.intel.com>
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <glib.h>
33 #include <cogl/cogl.h>
34 #include "cogl-pango-pipeline-cache.h"
35
36 #include "cogl/cogl-context-private.h"
37
38 typedef struct _CoglPangoPipelineCacheEntry CoglPangoPipelineCacheEntry;
39
40 struct _CoglPangoPipelineCache
41 {
42   GHashTable *hash_table;
43
44   CoglPipeline *base_texture_alpha_pipeline;
45   CoglPipeline *base_texture_rgba_pipeline;
46
47   gboolean use_mipmapping;
48 };
49
50 struct _CoglPangoPipelineCacheEntry
51 {
52   /* This will take a reference or it can be NULL to represent the
53      pipeline used to render colors */
54   CoglTexture *texture;
55
56   /* This will only take a weak reference */
57   CoglHandle pipeline;
58 };
59
60 static void
61 _cogl_pango_pipeline_cache_key_destroy (gpointer data)
62 {
63   if (data)
64     cogl_object_unref (data);
65 }
66
67 static void
68 _cogl_pango_pipeline_cache_value_destroy (gpointer data)
69 {
70   CoglPangoPipelineCacheEntry *cache_entry = data;
71
72   if (cache_entry->texture)
73     cogl_object_unref (cache_entry->texture);
74
75   /* We don't need to unref the pipeline because it only takes a weak
76      reference */
77
78   g_slice_free (CoglPangoPipelineCacheEntry, cache_entry);
79 }
80
81 CoglPangoPipelineCache *
82 _cogl_pango_pipeline_cache_new (gboolean use_mipmapping)
83 {
84   CoglPangoPipelineCache *cache = g_new (CoglPangoPipelineCache, 1);
85
86   /* The key is the pipeline pointer. A reference is taken when the
87      pipeline is used as a key so we should unref it again in the
88      destroy function */
89   cache->hash_table =
90     g_hash_table_new_full (g_direct_hash,
91                            g_direct_equal,
92                            _cogl_pango_pipeline_cache_key_destroy,
93                            _cogl_pango_pipeline_cache_value_destroy);
94
95   cache->base_texture_rgba_pipeline = NULL;
96   cache->base_texture_alpha_pipeline = NULL;
97
98   cache->use_mipmapping = use_mipmapping;
99
100   return cache;
101 }
102
103 static CoglPipeline *
104 get_base_texture_rgba_pipeline (CoglPangoPipelineCache *cache)
105 {
106   if (cache->base_texture_rgba_pipeline == NULL)
107     {
108       CoglPipeline *pipeline;
109
110       _COGL_GET_CONTEXT (ctx, NULL);
111
112       pipeline = cache->base_texture_rgba_pipeline = cogl_pipeline_new (ctx);
113
114       cogl_pipeline_set_layer_wrap_mode (pipeline, 0,
115                                          COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
116
117       if (cache->use_mipmapping)
118         cogl_pipeline_set_layer_filters
119           (pipeline, 0,
120            COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR,
121            COGL_PIPELINE_FILTER_LINEAR);
122     }
123
124   return cache->base_texture_rgba_pipeline;
125 }
126
127 static CoglPipeline *
128 get_base_texture_alpha_pipeline (CoglPangoPipelineCache *cache)
129 {
130   if (cache->base_texture_alpha_pipeline == NULL)
131     {
132       CoglPipeline *pipeline;
133
134       pipeline = cogl_pipeline_copy (get_base_texture_rgba_pipeline (cache));
135       cache->base_texture_alpha_pipeline = pipeline;
136
137       /* The default combine mode of materials is to modulate (A x B)
138        * the texture RGBA channels with the RGBA channels of the
139        * previous layer (which in our case is just the font color)
140        *
141        * Since the RGB for an alpha texture is defined as 0, this gives us:
142        *
143        *  result.rgb = color.rgb * 0
144        *  result.a = color.a * texture.a
145        *
146        * What we want is premultiplied rgba values:
147        *
148        *  result.rgba = color.rgb * texture.a
149        *  result.a = color.a * texture.a
150        */
151       cogl_pipeline_set_layer_combine (pipeline, 0, /* layer */
152                                        "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
153                                        NULL);
154     }
155
156   return cache->base_texture_alpha_pipeline;
157 }
158
159 typedef struct
160 {
161   CoglPangoPipelineCache *cache;
162   CoglTexture *texture;
163 } PipelineDestroyNotifyData;
164
165 static void
166 pipeline_destroy_notify_cb (void *user_data)
167 {
168   PipelineDestroyNotifyData *data = user_data;
169
170   g_hash_table_remove (data->cache->hash_table, data->texture);
171   g_slice_free (PipelineDestroyNotifyData, data);
172 }
173
174 CoglPipeline *
175 _cogl_pango_pipeline_cache_get (CoglPangoPipelineCache *cache,
176                                 CoglHandle texture)
177 {
178   CoglPangoPipelineCacheEntry *entry;
179   PipelineDestroyNotifyData *destroy_data;
180   static CoglUserDataKey pipeline_destroy_notify_key;
181
182   /* Look for an existing entry */
183   entry = g_hash_table_lookup (cache->hash_table, texture);
184
185   if (entry)
186     return cogl_object_ref (entry->pipeline);
187
188   /* No existing pipeline was found so let's create another */
189   entry = g_slice_new (CoglPangoPipelineCacheEntry);
190
191   if (texture)
192     {
193       CoglPipeline *base;
194
195       entry->texture = cogl_object_ref (texture);
196
197       if (cogl_texture_get_format (entry->texture) == COGL_PIXEL_FORMAT_A_8)
198         base = get_base_texture_alpha_pipeline (cache);
199       else
200         base = get_base_texture_rgba_pipeline (cache);
201
202       entry->pipeline = cogl_pipeline_copy (base);
203
204       cogl_pipeline_set_layer_texture (entry->pipeline, 0 /* layer */, texture);
205     }
206   else
207     {
208       _COGL_GET_CONTEXT (ctx, NULL);
209
210       entry->texture = NULL;
211       entry->pipeline = cogl_pipeline_new (ctx);
212     }
213
214   /* Add a weak reference to the pipeline so we can remove it from the
215      hash table when it is destroyed */
216   destroy_data = g_slice_new (PipelineDestroyNotifyData);
217   destroy_data->cache = cache;
218   destroy_data->texture = texture;
219   cogl_object_set_user_data (entry->pipeline,
220                              &pipeline_destroy_notify_key,
221                              destroy_data,
222                              pipeline_destroy_notify_cb);
223
224   g_hash_table_insert (cache->hash_table,
225                        texture ? cogl_object_ref (texture) : NULL,
226                        entry);
227
228   /* This doesn't take a reference on the pipeline so that it will use
229      the newly created reference */
230   return entry->pipeline;
231 }
232
233 void
234 _cogl_pango_pipeline_cache_free (CoglPangoPipelineCache *cache)
235 {
236   if (cache->base_texture_rgba_pipeline)
237     cogl_object_unref (cache->base_texture_rgba_pipeline);
238   if (cache->base_texture_alpha_pipeline)
239     cogl_object_unref (cache->base_texture_alpha_pipeline);
240
241   g_hash_table_destroy (cache->hash_table);
242
243   g_free (cache);
244 }