Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / image.c
blob01bd18f4d62bc0297982452279c34f32f068cc40
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "debug.h"
26 #include "image.h"
28 typedef struct {
29 gchar *path;
31 GBytes *contents;
33 const gchar *extension;
34 const gchar *mime;
35 gchar *gen_filename;
36 gchar *friendly_filename;
37 } PurpleImagePrivate;
39 enum {
40 PROP_0,
41 PROP_PATH,
42 PROP_CONTENTS,
43 PROP_SIZE,
44 PROP_LAST
47 static GParamSpec *properties[PROP_LAST];
49 G_DEFINE_TYPE_WITH_PRIVATE(PurpleImage, purple_image, G_TYPE_OBJECT);
51 /******************************************************************************
52 * Helpers
53 ******************************************************************************/
54 static void
55 _purple_image_set_path(PurpleImage *image, const gchar *path) {
56 PurpleImagePrivate *priv = purple_image_get_instance_private(image);
58 g_free(priv->path);
60 priv->path = g_strdup(path);
63 static void
64 _purple_image_set_contents(PurpleImage *image, GBytes *bytes) {
65 PurpleImagePrivate *priv = purple_image_get_instance_private(image);
67 if(priv->contents)
68 g_bytes_unref(priv->contents);
70 priv->contents = (bytes) ? g_bytes_ref(bytes) : NULL;
73 /******************************************************************************
74 * Object stuff
75 ******************************************************************************/
76 static void
77 purple_image_init(PurpleImage *image) {
80 static void
81 purple_image_finalize(GObject *obj) {
82 PurpleImage *image = PURPLE_IMAGE(obj);
83 PurpleImagePrivate *priv = purple_image_get_instance_private(image);
85 if(priv->contents)
86 g_bytes_unref(priv->contents);
88 g_free(priv->path);
89 g_free(priv->gen_filename);
90 g_free(priv->friendly_filename);
92 G_OBJECT_CLASS(purple_image_parent_class)->finalize(obj);
95 static void
96 purple_image_set_property(GObject *obj, guint param_id,
97 const GValue *value, GParamSpec *pspec)
99 PurpleImage *image = PURPLE_IMAGE(obj);
101 switch (param_id) {
102 case PROP_PATH:
103 _purple_image_set_path(image, g_value_get_string(value));
104 break;
105 case PROP_CONTENTS:
106 _purple_image_set_contents(image, g_value_get_boxed(value));
107 break;
108 default:
109 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
110 break;
114 static void
115 purple_image_get_property(GObject *obj, guint param_id, GValue *value,
116 GParamSpec *pspec)
118 PurpleImage *image = PURPLE_IMAGE(obj);
120 switch (param_id) {
121 case PROP_PATH:
122 g_value_set_string(value, purple_image_get_path(image));
123 break;
124 case PROP_CONTENTS:
125 g_value_set_boxed(value, purple_image_get_contents(image));
126 break;
127 case PROP_SIZE:
128 g_value_set_uint64(value, purple_image_get_data_size(image));
129 break;
130 default:
131 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
132 break;
136 static void
137 purple_image_class_init(PurpleImageClass *klass) {
138 GObjectClass *gobj_class = G_OBJECT_CLASS(klass);
140 gobj_class->finalize = purple_image_finalize;
141 gobj_class->get_property = purple_image_get_property;
142 gobj_class->set_property = purple_image_set_property;
144 properties[PROP_PATH] = g_param_spec_string(
145 "path",
146 "path",
147 "The filepath for the image if one was provided",
148 NULL,
149 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
152 properties[PROP_CONTENTS] = g_param_spec_boxed(
153 "contents",
154 "contents",
155 "The contents of the image stored in a GBytes",
156 G_TYPE_BYTES,
157 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS
160 properties[PROP_SIZE] = g_param_spec_uint64(
161 "size",
162 "size",
163 "The size of the image in bytes",
165 G_MAXUINT64,
167 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS
170 g_object_class_install_properties(gobj_class, PROP_LAST, properties);
173 /******************************************************************************
174 * API
175 ******************************************************************************/
176 PurpleImage *
177 purple_image_new_from_bytes(GBytes *bytes) {
178 return g_object_new(
179 PURPLE_TYPE_IMAGE,
180 "contents", bytes,
181 NULL
185 PurpleImage *
186 purple_image_new_from_file(const gchar *path, GError **error) {
187 PurpleImage *image = NULL;
188 GBytes *bytes = NULL;
189 gchar *contents = NULL;
190 gsize length = 0;
192 if(!g_file_get_contents(path, &contents, &length, error)) {
193 return NULL;
196 bytes = g_bytes_new_take(contents, length);
198 image = g_object_new(
199 PURPLE_TYPE_IMAGE,
200 "contents", bytes,
201 "path", path,
202 NULL
205 g_bytes_unref(bytes);
207 return image;
210 PurpleImage *
211 purple_image_new_from_data(const guint8 *data, gsize length) {
212 PurpleImage *image;
213 GBytes *bytes = NULL;
215 bytes = g_bytes_new(data, length);
217 image = purple_image_new_from_bytes(bytes);
219 g_bytes_unref(bytes);
221 return image;
224 PurpleImage *
225 purple_image_new_take_data(guint8 *data, gsize length) {
226 PurpleImage *image;
227 GBytes *bytes = NULL;
229 bytes = g_bytes_new_take(data, length);
231 image = purple_image_new_from_bytes(bytes);
233 g_bytes_unref(bytes);
235 return image;
238 gboolean
239 purple_image_save(PurpleImage *image, const gchar *path) {
240 PurpleImagePrivate *priv = NULL;
241 gconstpointer data;
242 gsize len;
243 gboolean succ;
245 g_return_val_if_fail(PURPLE_IS_IMAGE(image), FALSE);
246 g_return_val_if_fail(path != NULL, FALSE);
247 g_return_val_if_fail(path[0] != '\0', FALSE);
249 priv = purple_image_get_instance_private(image);
250 data = purple_image_get_data(image);
251 len = purple_image_get_data_size(image);
253 g_return_val_if_fail(data != NULL, FALSE);
254 g_return_val_if_fail(len > 0, FALSE);
256 succ = purple_util_write_data_to_file_absolute(path, data, len);
257 if (succ && priv->path == NULL)
258 priv->path = g_strdup(path);
260 return succ;
263 GBytes *
264 purple_image_get_contents(PurpleImage *image)
266 PurpleImagePrivate *priv = NULL;
268 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
270 priv = purple_image_get_instance_private(image);
272 if(priv->contents)
273 return g_bytes_ref(priv->contents);
275 return NULL;
278 const gchar *
279 purple_image_get_path(PurpleImage *image) {
280 PurpleImagePrivate *priv = NULL;
282 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
284 priv = purple_image_get_instance_private(image);
286 return priv->path ? priv->path : purple_image_generate_filename(image);
289 gsize
290 purple_image_get_data_size(PurpleImage *image) {
291 PurpleImagePrivate *priv;
293 g_return_val_if_fail(PURPLE_IS_IMAGE(image), 0);
295 priv = purple_image_get_instance_private(image);
297 if(priv->contents)
298 return g_bytes_get_size(priv->contents);
300 return 0;
303 gconstpointer
304 purple_image_get_data(PurpleImage *image) {
305 PurpleImagePrivate *priv = NULL;
307 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
309 priv = purple_image_get_instance_private(image);
311 if(priv->contents)
312 return g_bytes_get_data(priv->contents, NULL);
314 return NULL;
317 const gchar *
318 purple_image_get_extension(PurpleImage *image) {
319 PurpleImagePrivate *priv = NULL;
320 gconstpointer data;
322 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
324 priv = purple_image_get_instance_private(image);
326 if (priv->extension)
327 return priv->extension;
329 if (purple_image_get_data_size(image) < 4)
330 return NULL;
332 data = purple_image_get_data(image);
333 g_assert(data != NULL);
335 if (memcmp(data, "GIF8", 4) == 0)
336 return priv->extension = "gif";
337 if (memcmp(data, "\xff\xd8\xff", 3) == 0) /* 4th may be e0 through ef */
338 return priv->extension = "jpg";
339 if (memcmp(data, "\x89PNG", 4) == 0)
340 return priv->extension = "png";
341 if (memcmp(data, "MM", 2) == 0)
342 return priv->extension = "tif";
343 if (memcmp(data, "II", 2) == 0)
344 return priv->extension = "tif";
345 if (memcmp(data, "BM", 2) == 0)
346 return priv->extension = "bmp";
347 if (memcmp(data, "\x00\x00\x01\x00", 4) == 0)
348 return priv->extension = "ico";
350 return NULL;
353 const gchar *
354 purple_image_get_mimetype(PurpleImage *image) {
355 PurpleImagePrivate *priv = NULL;
356 const gchar *ext = purple_image_get_extension(image);
358 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
360 priv = purple_image_get_instance_private(image);
362 if (priv->mime)
363 return priv->mime;
365 g_return_val_if_fail(ext != NULL, NULL);
367 if (g_strcmp0(ext, "gif") == 0)
368 return priv->mime = "image/gif";
369 if (g_strcmp0(ext, "jpg") == 0)
370 return priv->mime = "image/jpeg";
371 if (g_strcmp0(ext, "png") == 0)
372 return priv->mime = "image/png";
373 if (g_strcmp0(ext, "tif") == 0)
374 return priv->mime = "image/tiff";
375 if (g_strcmp0(ext, "bmp") == 0)
376 return priv->mime = "image/bmp";
377 if (g_strcmp0(ext, "ico") == 0)
378 return priv->mime = "image/vnd.microsoft.icon";
380 return NULL;
383 const gchar *
384 purple_image_generate_filename(PurpleImage *image) {
385 PurpleImagePrivate *priv = NULL;
386 gconstpointer data;
387 gsize len;
388 const gchar *ext = NULL;
389 gchar *checksum;
391 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
393 priv = purple_image_get_instance_private(image);
395 if (priv->gen_filename)
396 return priv->gen_filename;
398 /* grab the image's data and size of that data */
399 data = purple_image_get_data(image);
400 len = purple_image_get_data_size(image);
402 /* create a checksum of it and use it as the start of our filename */
403 checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data, len);
405 /* if the image has a known format, set the extension appropriately */
406 ext = purple_image_get_extension(image);
407 if(ext != NULL) {
408 priv->gen_filename = g_strdup_printf("%s.%s", checksum, ext);
409 g_free(checksum);
410 } else {
411 priv->gen_filename = checksum;
414 return priv->gen_filename;
417 void
418 purple_image_set_friendly_filename(PurpleImage *image, const gchar *filename) {
419 PurpleImagePrivate *priv = NULL;
420 gchar *newname;
421 const gchar *escaped;
423 g_return_if_fail(PURPLE_IS_IMAGE(image));
425 priv = purple_image_get_instance_private(image);
427 newname = g_path_get_basename(filename);
428 escaped = purple_escape_filename(newname);
429 g_free(newname);
430 newname = NULL;
432 if (g_strcmp0(escaped, "") == 0 || g_strcmp0(escaped, ".") == 0 ||
433 g_strcmp0(escaped, G_DIR_SEPARATOR_S) == 0 ||
434 g_strcmp0(escaped, "/") == 0 || g_strcmp0(escaped, "\\") == 0)
436 escaped = NULL;
439 g_free(priv->friendly_filename);
440 priv->friendly_filename = g_strdup(escaped);
443 const gchar *
444 purple_image_get_friendly_filename(PurpleImage *image) {
445 PurpleImagePrivate *priv = NULL;
447 g_return_val_if_fail(PURPLE_IS_IMAGE(image), NULL);
449 priv = purple_image_get_instance_private(image);
451 if(priv->friendly_filename) {
452 return priv->friendly_filename;
455 return purple_image_generate_filename(image);