2009-11-13 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / bitmapimage.cpp
blob2eb99cc486546fff2706084ce8f0d30042a8f1c2
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * bitmapimage.cpp
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007-2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
14 #include <config.h>
16 #include <glib/gstdio.h>
17 #include <fcntl.h>
18 #include <errno.h>
20 #include "application.h"
21 #include "bitmapimage.h"
22 #include "deployment.h"
23 #include "runtime.h"
24 #include "uri.h"
25 #include "debug.h"
27 #ifdef WORDS_BIGENDIAN
28 #define set_pixel_bgra(pixel,index,b,g,r,a) \
29 G_STMT_START { \
30 ((unsigned char *)(pixel))[index] = a; \
31 ((unsigned char *)(pixel))[index+1] = r; \
32 ((unsigned char *)(pixel))[index+2] = g; \
33 ((unsigned char *)(pixel))[index+3] = b; \
34 } G_STMT_END
35 #define get_pixel_bgr_p(p,b,g,r) \
36 G_STMT_START { \
37 r = *(p); \
38 g = *(p+1); \
39 b = *(p+2); \
40 } G_STMT_END
41 #else
42 #define set_pixel_bgra(pixel,index,b,g,r,a) \
43 G_STMT_START { \
44 ((unsigned char *)(pixel))[index] = b; \
45 ((unsigned char *)(pixel))[index+1] = g; \
46 ((unsigned char *)(pixel))[index+2] = r; \
47 ((unsigned char *)(pixel))[index+3] = a; \
48 } G_STMT_END
49 #define get_pixel_bgr_p(p,b,g,r) \
50 G_STMT_START { \
51 b = *(p); \
52 g = *(p+1); \
53 r = *(p+2); \
54 } G_STMT_END
55 #endif
56 #define get_pixel_bgra(color, b, g, r, a) \
57 G_STMT_START { \
58 a = *(p+3); \
59 r = *(p+2); \
60 g = *(p+1); \
61 b = *(p+0); \
62 } G_STMT_END
63 #include "alpha-premul-table.inc"
66 // Expands RGB to ARGB allocating new buffer for it.
68 static gpointer
69 expand_rgb_to_argb (GdkPixbuf *pixbuf)
71 guchar *pb_pixels = gdk_pixbuf_get_pixels (pixbuf);
72 guchar *p;
73 int w = gdk_pixbuf_get_width (pixbuf);
74 int h = gdk_pixbuf_get_height (pixbuf);
75 int stride = w * 4;
76 guchar *data = (guchar *) g_malloc (stride * h);
77 guchar *out;
79 for (int y = 0; y < h; y ++) {
80 p = pb_pixels + y * gdk_pixbuf_get_rowstride (pixbuf);
81 out = data + y * (stride);
82 for (int x = 0; x < w; x ++) {
83 guchar r, g, b;
85 get_pixel_bgr_p (p, b, g, r);
86 set_pixel_bgra (out, 0, r, g, b, 255);
88 p += 3;
89 out += 4;
93 return (gpointer) data;
97 // Converts RGBA unmultiplied alpha to ARGB pre-multiplied alpha.
99 static gpointer
100 premultiply_rgba (GdkPixbuf *pixbuf)
102 guchar *pb_pixels = gdk_pixbuf_get_pixels (pixbuf);
103 guchar *p;
104 int w = gdk_pixbuf_get_width (pixbuf);
105 int h = gdk_pixbuf_get_height (pixbuf);
106 int stride = w * 4;
107 guchar *data = (guchar *) g_malloc (stride * h);
108 guchar *out;
110 for (int y = 0; y < h; y ++) {
111 p = pb_pixels + y * gdk_pixbuf_get_rowstride (pixbuf);
112 out = data + y * (stride);
113 for (int x = 0; x < w; x ++) {
114 guchar r, g, b, a;
116 get_pixel_bgra (p, b, g, r, a);
118 /* pre-multipled alpha */
119 if (a == 0) {
120 r = g = b = 0;
122 else if (a < 255) {
123 r = pre_multiplied_table [r][a];
124 g = pre_multiplied_table [g][a];
125 b = pre_multiplied_table [b][a];
128 /* store it back, swapping red and blue */
129 set_pixel_bgra (out, 0, r, g, b, a);
131 p += 4;
132 out += 4;
136 return (gpointer) data;
139 BitmapImage::BitmapImage ()
141 SetObjectType (Type::BITMAPIMAGE);
142 downloader = NULL;
143 loader = NULL;
144 gerror = NULL;
145 part_name = NULL;
146 get_res_aborter = NULL;
147 policy = MediaPolicy;
150 BitmapImage::~BitmapImage ()
152 if (downloader)
153 downloader->unref ();
155 if (part_name)
156 g_free (part_name);
158 if (get_res_aborter)
159 delete get_res_aborter;
161 CleanupLoader ();
164 void
165 BitmapImage::Dispose ()
167 Abort ();
168 BitmapSource::Dispose ();
171 void
172 BitmapImage::Abort ()
174 if (downloader) {
175 CleanupDownloader ();
176 downloader->Abort ();
177 downloader->unref ();
178 downloader = NULL;
181 if (get_res_aborter)
182 get_res_aborter->Cancel ();
185 void
186 BitmapImage::uri_source_changed_callback (EventObject *user_data)
188 BitmapImage *image = (BitmapImage *) user_data;
189 image->UriSourceChanged ();
192 static void
193 resource_notify (NotifyType type, gint64 args, gpointer user_data)
195 BitmapImage *media = (BitmapImage *) user_data;
197 if (type == NotifyProgressChanged)
198 media->SetProgress ((double)(args)/100.0);
199 else if (type == NotifyFailed)
200 media->DownloaderFailed ();
201 else if (type == NotifyCompleted)
202 media->DownloaderComplete ();
205 void
206 BitmapImage::UriSourceChanged ()
208 Surface *surface = Deployment::GetCurrent ()->GetSurface ();
209 Application *current = Application::GetCurrent ();
210 Uri *uri = GetUriSource ();
212 if (surface == NULL) {
213 SetBitmapData (NULL);
214 return;
217 if (current && uri) {
218 if (get_res_aborter)
219 delete get_res_aborter;
220 get_res_aborter = new Cancellable ();
221 if (!current->GetResource (GetResourceBase(), uri, resource_notify, pixbuf_write, policy, get_res_aborter, this))
222 DownloaderFailed ();
226 void
227 BitmapImage::OnPropertyChanged (PropertyChangedEventArgs *args, MoonError *error)
229 if (args->GetProperty ()->GetOwnerType () != Type::BITMAPIMAGE) {
230 BitmapSource::OnPropertyChanged (args, error);
231 return;
234 if (args->GetId () == BitmapImage::UriSourceProperty) {
235 Uri *uri = args->GetNewValue () ? args->GetNewValue ()->AsUri () : NULL;
237 Abort ();
239 if (Uri::IsNullOrEmpty (uri)) {
240 SetBitmapData (NULL);
241 } else if (uri->IsInvalidPath ()) {
242 if (IsBeingParsed ())
243 MoonError::FillIn (error, MoonError::ARGUMENT_OUT_OF_RANGE, 0, "invalid path found in uri");
244 SetBitmapData (NULL);
245 } else {
246 AddTickCall (uri_source_changed_callback);
248 } else if (args->GetId () == BitmapImage::ProgressProperty) {
249 Emit (DownloadProgressEvent, new DownloadProgressEventArgs (GetProgress ()));
252 NotifyListenersOfPropertyChange (args, error);
255 bool
256 BitmapImage::ValidateDownloadPolicy ()
258 Surface *surface = Deployment::GetCurrent ()->GetSurface ();
259 Uri *uri = GetUriSource ();
260 const char *location;
262 if (!uri)
263 return true;
265 if (!(location = GetDeployment ()->GetXapLocation ()))
266 location = surface ? surface->GetSourceLocation () : NULL;
268 return Downloader::ValidateDownloadPolicy (location, uri, policy);
271 void
272 BitmapImage::SetDownloader (Downloader *downloader, Uri *uri, const char *part_name)
274 Abort ();
276 this->downloader = downloader;
277 this->part_name = g_strdup (part_name);
279 downloader->ref();
281 downloader->AddHandler (Downloader::DownloadProgressChangedEvent, downloader_progress_changed, this);
282 downloader->AddHandler (Downloader::DownloadFailedEvent, downloader_failed, this);
283 downloader->AddHandler (Downloader::CompletedEvent, downloader_complete, this);
285 if (downloader->Completed ()) {
286 DownloaderComplete ();
287 } else {
288 if (!downloader->Started () && uri) {
289 downloader->Open ("GET", uri, policy);
290 downloader->SetStreamFunctions (pixbuf_write, NULL, this);
291 downloader->Send ();
296 void
297 BitmapImage::CleanupDownloader ()
299 downloader->RemoveHandler (Downloader::DownloadProgressChangedEvent, downloader_progress_changed, this);
300 downloader->RemoveHandler (Downloader::DownloadFailedEvent, downloader_failed, this);
301 downloader->RemoveHandler (Downloader::CompletedEvent, downloader_complete, this);
304 void
305 BitmapImage::downloader_progress_changed (EventObject *sender, EventArgs *calldata, gpointer closure)
307 BitmapImage *media = (BitmapImage *) closure;
309 media->DownloaderProgressChanged ();
312 void
313 BitmapImage::downloader_complete (EventObject *sender, EventArgs *calldata, gpointer closure)
315 BitmapImage *media = (BitmapImage *) closure;
317 media->DownloaderComplete ();
320 void
321 BitmapImage::downloader_failed (EventObject *sender, EventArgs *calldata, gpointer closure)
323 BitmapImage *media = (BitmapImage *) closure;
325 media->DownloaderFailed ();
328 void
329 BitmapImage::DownloaderProgressChanged ()
331 SetProgress (downloader->GetDownloadProgress ());
334 void
335 BitmapImage::DownloaderComplete ()
337 MoonError moon_error;
339 if (downloader)
340 CleanupDownloader ();
342 SetProgress (1.0);
344 if (downloader && loader == NULL) {
345 char *filename = downloader->GetDownloadedFilename (part_name);
347 if (filename == NULL) {
348 guchar *buffer = (guchar *)downloader->GetBuffer ();
350 if (buffer == NULL) {
351 MoonError::FillIn (&moon_error, MoonError::EXCEPTION, 4001, "downloader buffer was NULL");
352 goto failed;
355 PixbufWrite (buffer, 0, downloader->GetSize ());
356 } else {
357 guchar b[4096];
358 int offset = 0;
359 ssize_t n;
360 int fd;
362 if ((fd = g_open (filename, O_RDONLY)) == -1) {
363 MoonError::FillIn (&moon_error, MoonError::EXCEPTION, 4001, "failed to open file");
364 goto failed;
367 do {
368 do {
369 n = read (fd, b, sizeof (b));
370 } while (n == -1 && errno == EINTR);
372 if (n == -1) break;
374 PixbufWrite (b, offset, n);
376 offset += n;
377 } while (n > 0 && !gerror);
379 close (fd);
381 if (gerror) {
382 MoonError::FillIn (&moon_error, MoonError::EXCEPTION, 4001, gerror->message);
383 goto failed;
388 if (downloader) {
389 downloader->unref ();
390 downloader = NULL;
393 PixmapComplete ();
395 return;
396 failed:
397 downloader->unref ();
398 downloader = NULL;
400 if (loader)
401 gdk_pixbuf_loader_close (loader, NULL);
402 CleanupLoader ();
404 Emit (ImageFailedEvent, new ImageErrorEventArgs (moon_error));
408 void
409 BitmapImage::PixmapComplete ()
411 MoonError moon_error;
413 SetProgress (1.0);
415 if (!loader) goto failed;
417 gdk_pixbuf_loader_close (loader, gerror == NULL ? &gerror : NULL);
419 if (gerror) {
420 MoonError::FillIn (&moon_error, MoonError::EXCEPTION, 4001, gerror->message);
421 goto failed;
425 GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
427 if (pixbuf == NULL) {
428 MoonError::FillIn (&moon_error, MoonError::EXCEPTION, 4001, "failed to create image data");
429 goto failed;
432 SetPixelWidth (gdk_pixbuf_get_width (pixbuf));
433 SetPixelHeight (gdk_pixbuf_get_height (pixbuf));
435 if (gdk_pixbuf_get_n_channels (pixbuf) == 4) {
436 SetPixelFormat (PixelFormatPbgra32);
437 SetBitmapData (premultiply_rgba (pixbuf));
438 } else {
439 SetPixelFormat (PixelFormatBgr32);
440 SetBitmapData (expand_rgb_to_argb (pixbuf));
443 Invalidate ();
445 g_object_unref (loader);
446 loader = NULL;
448 Emit (ImageOpenedEvent, new RoutedEventArgs ());
450 return;
453 failed:
454 CleanupLoader ();
456 Emit (ImageFailedEvent, new ImageErrorEventArgs (moon_error));
459 void
460 BitmapImage::DownloaderFailed ()
462 Abort ();
463 Emit (ImageFailedEvent, new ImageErrorEventArgs (MoonError (MoonError::EXCEPTION, 4001, "downloader failed")));
466 void
467 BitmapImage::CleanupLoader ()
469 SetPixelWidth (0);
470 SetPixelHeight (0);
472 if (loader) {
473 g_object_unref (loader);
474 loader = NULL;
476 if (gerror) {
477 g_error_free (gerror);
478 gerror = NULL;
482 void
483 BitmapImage::CreateLoader (unsigned char *buffer)
485 if (!(moonlight_flags & RUNTIME_INIT_ALL_IMAGE_FORMATS)) {
486 // 89 50 4E 47 == png magic
487 if (buffer[0] == 0x89)
488 loader = gdk_pixbuf_loader_new_with_type ("png", NULL);
489 // ff d8 ff e0 == jfif magic
490 else if (buffer[0] == 0xff)
491 loader = gdk_pixbuf_loader_new_with_type ("jpeg", NULL);
493 else {
494 Abort ();
495 Emit (ImageFailedEvent, new ImageErrorEventArgs (MoonError (MoonError::EXCEPTION, 4001, "unsupported image type")));
497 } else {
498 loader = gdk_pixbuf_loader_new ();
502 void
503 BitmapImage::PixbufWrite (gpointer buffer, gint32 offset, gint32 n)
506 if (loader == NULL && offset == 0)
507 CreateLoader ((unsigned char *)buffer);
509 if (loader != NULL && gerror == NULL) {
510 gdk_pixbuf_loader_write (GDK_PIXBUF_LOADER (loader), (const guchar *)buffer, n, &gerror);
514 void
515 BitmapImage::pixbuf_write (void *buffer, gint32 offset, gint32 n, gpointer data)
517 BitmapImage *source = (BitmapImage *) data;
519 source->PixbufWrite ((unsigned char *)buffer, offset, n);