Skip to content

Commit

Permalink
fix(RCTImageView): Add missing features
Browse files Browse the repository at this point in the history
- resizeMode
- tintColor
- backgroundColor
- imageSize

Fixes #278, #366
  • Loading branch information
ebragge committed Jun 10, 2016
1 parent f0f1c10 commit b1dec49
Show file tree
Hide file tree
Showing 5 changed files with 567 additions and 76 deletions.
15 changes: 15 additions & 0 deletions Libraries/Image/Image.windows.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,21 @@ var Image = React.createClass({

statics: {
resizeMode: ImageResizeMode,

getSize(
url: string,
success: (width: number, height: number) => void,
failure: (error: any) => void,
) {
return ImageLoader.getSize(url)
.then(function(sizes) {
success(sizes.width, sizes.height);
})
.catch(failure || function() {
console.warn('Failed to get size for image: ' + url);
});
},

/**
* Prefetches a remote image for later use by downloading it to the disk
* cache
Expand Down
299 changes: 296 additions & 3 deletions ReactWindows/ReactNative/Modules/Image/ImageLoaderModule.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
using ReactNative.Bridge;
using Newtonsoft.Json.Linq;
using ReactNative.Bridge;
using ReactNative.Views.Image;
using System;
using System.Collections.Generic;
using Windows.ApplicationModel.Core;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;

namespace ReactNative.Modules.Image
{
class ImageLoaderModule : NativeModuleBase
{
private readonly Dictionary<BitmapImage, ExceptionRoutedEventHandler> _imageFailedHandlers =
new Dictionary<BitmapImage, ExceptionRoutedEventHandler>();

private readonly Dictionary<BitmapImage, RoutedEventHandler> _imageOpenedHandlers =
new Dictionary<BitmapImage, RoutedEventHandler>();

private readonly Dictionary<string, BitmapImage> _images =
new Dictionary<string, BitmapImage>();

private readonly Dictionary<string, byte[]> _data =
new Dictionary<string, byte[]>();

private readonly Dictionary<string, List<Tuple<Border, Color?, Color?>>> _waitListImage =
new Dictionary<string, List<Tuple<Border, Color?, Color?>>>();

private readonly Dictionary<string, List<Tuple<Border, Color?, Color?>>> _waitListData =
new Dictionary<string, List<Tuple<Border, Color?, Color?>>>();

public override string Name
{
get
Expand All @@ -12,11 +42,274 @@ public override string Name
}
}

internal BitmapImage GetImage(string uriString)
{
var image = default(BitmapImage);

if (_images.TryGetValue(uriString, out image))
{
return image;
}
else
{
return null;
}
}

internal byte[] GetData(string uriString)
{
var data = default(byte[]);

if (_data.TryGetValue(uriString, out data))
{
return data;
}
else
{
return null;
}
}

internal void LoadData(string uriString, ReactImageManager manager, object data)
{
lock (_waitListData)
{
var list = default(List<Tuple<Border, Color?, Color?>>);

if (_waitListData.TryGetValue(uriString, out list))
{
list.Add((Tuple<Border, Color?, Color?>)data);
return;
}
else
{
_waitListData.Add(uriString,
new List<Tuple<Border, Color?, Color?>>()
{
(Tuple<Border, Color?, Color?>)data
});
}
}

RunOnDispatcher(async () =>
{
var randomAccessStreamReference = RandomAccessStreamReference.CreateFromUri(new Uri(uriString));
var randomAccessStream = await randomAccessStreamReference.OpenReadAsync();
var decoder = await BitmapDecoder.CreateAsync(randomAccessStream);

var pixelData = await decoder.GetPixelDataAsync(
BitmapPixelFormat.Bgra8, // WriteableBitmap uses BGRA format
BitmapAlphaMode.Straight,
new BitmapTransform(),
ExifOrientationMode.RespectExifOrientation,
ColorManagementMode.DoNotColorManage
);

_data[uriString] = pixelData.DetachPixelData();

lock (_waitListData)
{
foreach (var item in _waitListData[uriString])
{
manager.DataLoaded(item, uriString);
}
_waitListData.Remove(uriString);
}
});
}

internal void LoadImage(string uriString, ReactImageManager manager, object data)
{
lock (_waitListImage)
{
var list = default(List<Tuple<Border, Color?, Color?>>);

if (_waitListImage.TryGetValue(uriString, out list))
{
list.Add((Tuple<Border, Color?, Color?>)data);
return;
}
else
{
_waitListImage.Add(uriString,
new List<Tuple<Border, Color?, Color?>>()
{
(Tuple<Border, Color?, Color?>)data
});
}
}

RunOnDispatcher(async () =>
{
var image = new BitmapImage();

var imageFailedHandler = new ExceptionRoutedEventHandler(
(sender, args) => OnFailed(uriString, image, manager, args));

image.ImageFailed += imageFailedHandler;

var imageOpenedHandler = new RoutedEventHandler(
(sender, args) => OnLoaded(uriString, image, manager, args));

image.ImageOpened += imageOpenedHandler;

_imageFailedHandlers.Add(image, imageFailedHandler);
_imageOpenedHandlers.Add(image, imageOpenedHandler);

var randomAccessStreamReference = RandomAccessStreamReference.CreateFromUri(new Uri(uriString));
var randomAccessStream = await randomAccessStreamReference.OpenReadAsync();

await image.SetSourceAsync(randomAccessStream);
});
}

[ReactMethod]
public void prefetchImage(string uriString, IPromise promise)
{
// TODO: (#366) Implement prefetch mechanism.
promise.Reject("Prefetch is not yet supported.");
RunOnDispatcher(async () =>
{
if (uriString == null || uriString.Length == 0)
{
promise.Reject("Cannot get the size of an image for an empty URI");
}

var image = new BitmapImage();

var imageFailedHandler = new ExceptionRoutedEventHandler(
(sender, args) => OnImageFailed(image, promise, args));

image.ImageFailed += imageFailedHandler;

var imageOpenedHandler = new RoutedEventHandler(
(sender, args) => OnImageFetched(uriString, image, promise, args));

image.ImageOpened += imageOpenedHandler;

_imageFailedHandlers.Add(image, imageFailedHandler);
_imageOpenedHandlers.Add(image, imageOpenedHandler);

var randomAccessStreamReference = RandomAccessStreamReference.CreateFromUri(new Uri(uriString));
var randomAccessStream = await randomAccessStreamReference.OpenReadAsync();

await image.SetSourceAsync(randomAccessStream);
});
}

[ReactMethod]
public void getSize(string uriString, IPromise promise)
{
RunOnDispatcher(async () =>
{
if (uriString == null || uriString.Length == 0)
{
promise.Reject("Cannot get the size of an image for an empty URI");
}

var image = new BitmapImage();

var imageFailedHandler = new ExceptionRoutedEventHandler(
(sender, args) => OnImageFailed(image, promise, args));

image.ImageFailed += imageFailedHandler;

var imageOpenedHandler = new RoutedEventHandler(
(sender, args) => OnImageLoaded(image, promise, args));

image.ImageOpened += imageOpenedHandler;

_imageFailedHandlers.Add(image, imageFailedHandler);
_imageOpenedHandlers.Add(image, imageOpenedHandler);

var randomAccessStreamReference = RandomAccessStreamReference.CreateFromUri(new Uri(uriString));
var randomAccessStream = await randomAccessStreamReference.OpenReadAsync();

await image.SetSourceAsync(randomAccessStream);
});
}

private void OnImageFailed(object sender, IPromise promise, ExceptionRoutedEventArgs args)
{
RemoveEventHandlers(sender as BitmapImage);
promise.Reject("Failure.");
}

private void OnImageLoaded(object sender, IPromise promise, RoutedEventArgs args)
{
var image = sender as BitmapImage;
RemoveEventHandlers(image);

var sizes = new JObject()
{
new JProperty("width", image.PixelWidth),
new JProperty("height",image.PixelHeight),
};

promise.Resolve(sizes);
}

private void OnImageFetched(string uriString, object sender, IPromise promise, RoutedEventArgs args)
{
var image = sender as BitmapImage;
RemoveEventHandlers(image);
_images[uriString] = image;

promise.Resolve(true);
}

private void OnFailed(string uriString, object sender, ReactImageManager manager, ExceptionRoutedEventArgs args)
{
lock(_waitListImage)
{
RemoveEventHandlers(sender as BitmapImage);

foreach (var item in _waitListImage[uriString])
{
manager.ImageLoaded(item, false, uriString);
}
_waitListImage.Remove(uriString);
}
}

private void OnLoaded(string uriString, object sender, ReactImageManager manager, RoutedEventArgs args)
{
lock (_waitListImage)
{
var image = sender as BitmapImage;
RemoveEventHandlers(image);
_images[uriString] = image;

foreach (var item in _waitListImage[uriString])
{
manager.ImageLoaded(item, true, uriString);
}
_waitListImage.Remove(uriString);
}
}

private void RemoveEventHandlers(BitmapImage image)
{
var imageFailedHandler = default(ExceptionRoutedEventHandler);
if (_imageFailedHandlers.TryGetValue(image, out imageFailedHandler))
{
_imageFailedHandlers.Remove(image);
image.ImageFailed -= imageFailedHandler;
}

var imageOpenedHandler = default(RoutedEventHandler);
if (_imageOpenedHandlers.TryGetValue(image, out imageOpenedHandler))
{
_imageOpenedHandlers.Remove(image);
image.ImageOpened -= imageOpenedHandler;
}
}

/// <summary>
/// Run action on UI thread.
/// </summary>
/// <param name="action">The action.</param>
private static async void RunOnDispatcher(DispatchedHandler action)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, action);
}
}
}
1 change: 1 addition & 0 deletions ReactWindows/ReactNative/ReactNative.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@
<Compile Include="UIManager\LayoutAnimation\LayoutAnimationController.cs" />
<Compile Include="UIManager\LayoutAnimation\LayoutUpdateAnimation.cs" />
<Compile Include="Views\Flip\ReactFlipViewManager.cs" />
<Compile Include="Views\Image\ReactImageShadowNode.cs" />
<Compile Include="Views\Progress\ReactProgressBarViewManager.cs" />
<Compile Include="Views\Progress\ProgressBarShadowNode.cs" />
<Compile Include="Views\Split\Events\SplitViewClosedEvent.cs" />
Expand Down
Loading

0 comments on commit b1dec49

Please sign in to comment.