« Posts by Jared

PokerSkills preview video

Getting close to the release of Poker Skills for iOS.

Poker Skills – New iOS App

1-iphone

Available for iphone and ipad July 1st!

We’ve been working hard on a new casual game called Poker Skills for Poker and Card Company One Widow.

‘Poker Skills’ is a new casual social game that challenges a players knowledge of poker. The game focuses on a players ability to recognize the highest value poker hands in a regular Texas holdem poker table. The hands start out easy with all community cards shown face up with two poker players hole cards also displayed face up. Selecting the highest hand at the table as the correct answer within the time limit increases your points and progresses a player through the game levels.

The Offical WebSite: http://www.onewidowpoker.com

Poker Skills - Home Screen

SC2 Loopback Free iOS

A new free version of SC2 loopback is now available for iOS.


sc2 loopback app store

- The full version has more channels available and no iAd banners

UIWebView to Image – iOS

For a recent project I needed to save a UIWebView to an image. Including not just the visible area but the whole scrollable view. There are a number of example for creating a PDF from a webview, but I couldn’t find many example of converting to an Image.

So below is a simple function to get it done. Includes some javascript calls to scroll the webview. This assumes the webview is created and visible to the user – again other example used a temporary uiwebview to render html content off screen.

Objective-C
 
- (UIImage*)webviewToImage:(UIWebView*)theWebView
{
    int webViewHeight = [[theWebView stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"] integerValue];
    int scrollByY = theWebView.frame.size.height;
    int imageName = 0;
 
    [theWebView stringByEvaluatingJavaScriptFromString:@"window.scrollTo(0,0);"];
 
    NSMutableArray* images = [[NSMutableArray alloc] init];
 
    CGRect screenRect = theWebView.frame;
    double currentWebViewHeight = webViewHeight;
    while (currentWebViewHeight > 0)
    {
        imageName ++;
 
        UIGraphicsBeginImageContext(screenRect.size);
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        [[UIColor blackColor] set];
        CGContextFillRect(ctx, screenRect);
 
        [theWebView.layer renderInContext:ctx];
 
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
 
        if(currentWebViewHeight < scrollByY)
        {
            CGRect lastImageRect = CGRectMake(0, scrollByY - currentWebViewHeight, theWebView.frame.size.width, currentWebViewHeight);
            CGImageRef imageRef = CGImageCreateWithImageInRect([newImage CGImage], lastImageRect);
 
            newImage = [UIImage imageWithCGImage:imageRef];
            CGImageRelease(imageRef);
        }
        [images addObject:newImage];
 
        [theWebView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.scrollBy(0,%d);", scrollByY]];
        currentWebViewHeight -= scrollByY;
    }
 
    [theWebView stringByEvaluatingJavaScriptFromString:@"window.scrollTo(0,0);"];
 
    UIImage *resultImage;
 
    if(images.count > 1) {
        //join all images together..
        CGSize sz;
        for(int i=0;i<images.count;i++) {
 
            sz.width = MAX(sz.width, ((UIImage*)[images objectAtIndex:i]).size.width );
            sz.height += ((UIImage*)[images objectAtIndex:i]).size.height;
        }
 
        UIGraphicsBeginImageContext(sz);
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        [[UIColor blackColor] set];
        CGContextFillRect(ctx, screenRect);
 
        int y=0;
        for(int i=0;i<images.count;i++) {
 
            UIImage* img = [images objectAtIndex:i];
            [img drawAtPoint:CGPointMake(0,y)];
            y += img.size.height;
        }
 
        resultImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    } else {
 
        resultImage = [images objectAtIndex:0];
    }
 
    return resultImage;
 
}

Happy New Year – SC2 Loopback free for a week

To kick off the new year SC2 Loopback is free for download for one week!

http://www.sc2loopback.com

Grab your copy now and enjoy the latest star craft videos on your iOS devices.

One widow playing cards

Some cool designs on the age old playing cards. Check them out, makes a great xmas idea. More apps and poker related software is in development so stay tuned!

onwidow.com

one widow playing cards

SC2 Loopback App

Just released a new app in the app store. A small iOS app for watching starcraft replays and videos.
http://ww.sc2loopback.com/

Its simple app that uses some external api’s and developed with xcode, supporting iOS 4.2 on iPhones and iPads. I’ll be releasing an Android version soon and maybe a windows desktop and metro version as well.

sc2 loopback app store

sc2 loopback

Silverlight 5 Slider Binding Bug

As described here
http://forums.silverlight.net/t/119338.aspx/1

Silverlight 5 (and 4) has a bug with it’s slider control. You can not bind to the Maximum and Minimum properties of the control. To get around this I have a simple solution with the code below. Basically just inherit from Slider and add two custom dependency properties and register for the on-changed events. In the callback you can manually set the minimum and maximum values of the slider instead of bindings.

This is a pain but seems to be a easy and clean solutions.

C#
 
public class CustomSlider : Slider
    {
 
        public static readonly DependencyProperty MinimumBugFixProperty = DependencyProperty.Register("MinimumBugFix", typeof(double), typeof(CustomSlider), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(OnUpdateSliderRange)));
        public static readonly DependencyProperty MaximumBugFixProperty = DependencyProperty.Register("MaximumBugFix", typeof(double), typeof(CustomSlider), new FrameworkPropertyMetadata(100, new PropertyChangedCallback(OnUpdateSliderRange)));
 
 
        public CustomSlider()
        {
 
        }
 
        public double MinimumBugFix
        {
            get { return (double)GetValue(MinimumBugFixProperty); }
            set { SetValue(MinimumBugFixProperty, value); }
        }
 
        public double MaximumBugFix
        {
            get { return (double)GetValue(MaximumBugFixProperty); }
            set { SetValue(MaximumBugFixProperty, value); }
        }
 
        private static void OnUpdateSliderRange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CustomSlider slider = d as CustomSlider;
            if (slider != null)
            {
                if(slider.Maximum != slider.MaximumBugFix)
                    slider.Maximum = slider.MaximumBugFix;
                if(slider.Minimum != slider.MinimumBugFix)
                    slider.Minimum = slider.MinimumBugFix;
            }
        }
}

WPF to Silverlight porting…

My biggest WTF moment so far with silverlight.. there is no Visibility.Hidden enum value.

So much for reusing all those valueconverters.. oh the pain!

C#
 
namespace System.Windows
{
    // Summary:
    //     Specifies the display state of an element.
    public enum Visibility
    {
        // Summary:
        //     Display the element.
        Visible = 0,
        //
        // Summary:
        //     Do not display the element, and do not reserve space for it in layout.
        Collapsed = 1,
    }
}

Calculate Dominant Color of Bitmap

The code section below is an example of how to calculate the dominant color in a bitmap. The code uses euclidean distance calculations for each color channel in each pixel. Then it sorts the pixels to find the color which is the closest to all other colors.

To improve the output I take to top %2 of colors and average them. Also the Alpha channel is treated differently though it could be changed to use the same distance logic.

The results are much better than the average color calculation. Though it is more computationally expensive so it will take longer to complete.

C#
<code>
 
public static Color CalculateDominantColor(BitmapSource source)
        {
            if (source.Format.BitsPerPixel != 32 || source.Format != PixelFormats.Bgra32)
                throw new ApplicationException("expected 32bit image");
 
 
            Dictionary<Color, double> colorDist = new Dictionary<Color, double>();
 
            System.Windows.Size sz = new System.Windows.Size(source.PixelWidth, source.PixelHeight);
 
            //read bitmap 
            int pixelsSz = (int)sz.Width * (int)sz.Height * (source.Format.BitsPerPixel / 8);
            int stride = ((int)sz.Width * source.Format.BitsPerPixel + 7) / 8;
            int pixelBytes = (source.Format.BitsPerPixel / 8);
 
            byte[] pixels = new byte[pixelsSz];
            source.CopyPixels(pixels, stride, 0);
 
            const int alphaThershold = 10;
            UInt64 pixelCount = 0;
            UInt64 avgAlpha = 0;
 
            for (int y = 0; y < sz.Height; y++)
            {
                for (int x = 0; x < sz.Width; x++)
                {
                    int index = (int)((y * sz.Width) + x) * (pixelBytes);
                    byte r1, g1, b1, a1; r1 = g1 = b1 = a1 = 0;
                    a1 = pixels[index + 3];
                    r1 = pixels[index + 2];
                    g1 = pixels[index + 1];
                    b1 = pixels[index];
 
                    if (a1 <= alphaThershold)
                        continue; //ignore
 
                    pixelCount++;
                    avgAlpha += (UInt64)a1;
 
                    Color cl = Color.FromArgb(0, r1, g1, b1);
                    double dist = 0;
                    if (!colorDist.ContainsKey(cl))
                    {
                        colorDist.Add(cl, 0);
 
                        for (int y2 = 0; y2 < sz.Height; y2++)
                        {
                            for (int x2 = 0; x2 < sz.Width; x2++)
                            {
                                int index2 = (int)(y2 * sz.Width) + x2;
                                byte r2, g2, b2, a2; r2 = g2 = b2 = a2 = 0;
                                a2 = pixels[index2 + 3];
                                r2 = pixels[index2 + 2];
                                g2 = pixels[index2 + 1];
                                b2 = pixels[index2];
 
                                if (a2 <= alphaThershold)
                                    continue; //ignore
 
                                dist += Math.Sqrt(Math.Pow(r2 - r1, 2) + 
                                                  Math.Pow(g2 - g1, 2) + 
                                                  Math.Pow(b2 - b1, 2));
                            }
                        }
 
                        colorDist[cl] = dist;
                    }
                }
            }
 
            //clamp alpha
            avgAlpha = avgAlpha / pixelCount;
            if (avgAlpha >= (255 - alphaThershold))
                avgAlpha = 255;
 
            //take weighted average of top 2% of colors         
            var clrs = (from entry in colorDist
                        orderby entry.Value ascending
                        select new { Color = entry.Key, Dist = 1.0/Math.Max(1,entry.Value) }).ToList().Take( Math.Max(1, (int)(colorDist.Count * 0.02)) );
 
            double sumDist = clrs.Sum(x => x.Dist);
            Color result = Color.FromArgb((byte)avgAlpha,
                                          (byte)(clrs.Sum(x => x.Color.R * x.Dist) / sumDist),
                                          (byte)(clrs.Sum(x => x.Color.G * x.Dist) / sumDist),
                                          (byte)(clrs.Sum(x => x.Color.B * x.Dist) / sumDist));
 
            return result;
        }
 
</code>