1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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.
16 #include <glib/gstdio.h>
20 #include "application.h"
21 #include "bitmapimage.h"
22 #include "deployment.h"
27 #ifdef WORDS_BIGENDIAN
28 #define set_pixel_bgra(pixel,index,b,g,r,a) \
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; \
35 #define get_pixel_bgr_p(p,b,g,r) \
42 #define set_pixel_bgra(pixel,index,b,g,r,a) \
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; \
49 #define get_pixel_bgr_p(p,b,g,r) \
56 #define get_pixel_bgra(color, b, g, r, a) \
63 #include "alpha-premul-table.inc"
66 // Expands RGB to ARGB allocating new buffer for it.
69 expand_rgb_to_argb (GdkPixbuf
*pixbuf
)
71 guchar
*pb_pixels
= gdk_pixbuf_get_pixels (pixbuf
);
73 int w
= gdk_pixbuf_get_width (pixbuf
);
74 int h
= gdk_pixbuf_get_height (pixbuf
);
76 guchar
*data
= (guchar
*) g_malloc (stride
* h
);
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
++) {
85 get_pixel_bgr_p (p
, b
, g
, r
);
86 set_pixel_bgra (out
, 0, r
, g
, b
, 255);
93 return (gpointer
) data
;
97 // Converts RGBA unmultiplied alpha to ARGB pre-multiplied alpha.
100 premultiply_rgba (GdkPixbuf
*pixbuf
)
102 guchar
*pb_pixels
= gdk_pixbuf_get_pixels (pixbuf
);
104 int w
= gdk_pixbuf_get_width (pixbuf
);
105 int h
= gdk_pixbuf_get_height (pixbuf
);
107 guchar
*data
= (guchar
*) g_malloc (stride
* h
);
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
++) {
116 get_pixel_bgra (p
, b
, g
, r
, a
);
118 /* pre-multipled alpha */
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
);
136 return (gpointer
) data
;
139 BitmapImage::BitmapImage ()
141 SetObjectType (Type::BITMAPIMAGE
);
146 get_res_aborter
= NULL
;
147 policy
= MediaPolicy
;
150 BitmapImage::~BitmapImage ()
153 downloader
->unref ();
159 delete get_res_aborter
;
165 BitmapImage::Dispose ()
168 BitmapSource::Dispose ();
172 BitmapImage::Abort ()
175 CleanupDownloader ();
176 downloader
->Abort ();
177 downloader
->unref ();
182 get_res_aborter
->Cancel ();
186 BitmapImage::uri_source_changed_callback (EventObject
*user_data
)
188 BitmapImage
*image
= (BitmapImage
*) user_data
;
189 image
->UriSourceChanged ();
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 ();
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
);
217 if (current
&& uri
) {
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))
227 BitmapImage::OnPropertyChanged (PropertyChangedEventArgs
*args
, MoonError
*error
)
229 if (args
->GetProperty ()->GetOwnerType () != Type::BITMAPIMAGE
) {
230 BitmapSource::OnPropertyChanged (args
, error
);
234 if (args
->GetId () == BitmapImage::UriSourceProperty
) {
235 Uri
*uri
= args
->GetNewValue () ? args
->GetNewValue ()->AsUri () : NULL
;
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
);
246 AddTickCall (uri_source_changed_callback
);
248 } else if (args
->GetId () == BitmapImage::ProgressProperty
) {
249 Emit (DownloadProgressEvent
, new DownloadProgressEventArgs (GetProgress ()));
252 NotifyListenersOfPropertyChange (args
, error
);
256 BitmapImage::ValidateDownloadPolicy ()
258 Surface
*surface
= Deployment::GetCurrent ()->GetSurface ();
259 Uri
*uri
= GetUriSource ();
260 const char *location
;
265 if (!(location
= GetDeployment ()->GetXapLocation ()))
266 location
= surface
? surface
->GetSourceLocation () : NULL
;
268 return Downloader::ValidateDownloadPolicy (location
, uri
, policy
);
272 BitmapImage::SetDownloader (Downloader
*downloader
, Uri
*uri
, const char *part_name
)
276 this->downloader
= downloader
;
277 this->part_name
= g_strdup (part_name
);
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 ();
288 if (!downloader
->Started () && uri
) {
289 downloader
->Open ("GET", uri
, policy
);
290 downloader
->SetStreamFunctions (pixbuf_write
, NULL
, this);
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);
305 BitmapImage::downloader_progress_changed (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
307 BitmapImage
*media
= (BitmapImage
*) closure
;
309 media
->DownloaderProgressChanged ();
313 BitmapImage::downloader_complete (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
315 BitmapImage
*media
= (BitmapImage
*) closure
;
317 media
->DownloaderComplete ();
321 BitmapImage::downloader_failed (EventObject
*sender
, EventArgs
*calldata
, gpointer closure
)
323 BitmapImage
*media
= (BitmapImage
*) closure
;
325 media
->DownloaderFailed ();
329 BitmapImage::DownloaderProgressChanged ()
331 SetProgress (downloader
->GetDownloadProgress ());
335 BitmapImage::DownloaderComplete ()
337 MoonError moon_error
;
340 CleanupDownloader ();
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");
355 PixbufWrite (buffer
, 0, downloader
->GetSize ());
362 if ((fd
= g_open (filename
, O_RDONLY
)) == -1) {
363 MoonError::FillIn (&moon_error
, MoonError::EXCEPTION
, 4001, "failed to open file");
369 n
= read (fd
, b
, sizeof (b
));
370 } while (n
== -1 && errno
== EINTR
);
374 PixbufWrite (b
, offset
, n
);
377 } while (n
> 0 && !gerror
);
382 MoonError::FillIn (&moon_error
, MoonError::EXCEPTION
, 4001, gerror
->message
);
389 downloader
->unref ();
397 downloader
->unref ();
401 gdk_pixbuf_loader_close (loader
, NULL
);
404 Emit (ImageFailedEvent
, new ImageErrorEventArgs (moon_error
));
409 BitmapImage::PixmapComplete ()
411 MoonError moon_error
;
415 if (!loader
) goto failed
;
417 gdk_pixbuf_loader_close (loader
, gerror
== NULL
? &gerror
: NULL
);
420 MoonError::FillIn (&moon_error
, MoonError::EXCEPTION
, 4001, gerror
->message
);
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");
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
));
439 SetPixelFormat (PixelFormatBgr32
);
440 SetBitmapData (expand_rgb_to_argb (pixbuf
));
445 g_object_unref (loader
);
448 Emit (ImageOpenedEvent
, new RoutedEventArgs ());
456 Emit (ImageFailedEvent
, new ImageErrorEventArgs (moon_error
));
460 BitmapImage::DownloaderFailed ()
463 Emit (ImageFailedEvent
, new ImageErrorEventArgs (MoonError (MoonError::EXCEPTION
, 4001, "downloader failed")));
467 BitmapImage::CleanupLoader ()
473 g_object_unref (loader
);
477 g_error_free (gerror
);
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
);
495 Emit (ImageFailedEvent
, new ImageErrorEventArgs (MoonError (MoonError::EXCEPTION
, 4001, "unsupported image type")));
498 loader
= gdk_pixbuf_loader_new ();
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
);
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
);