Alan Neveu created CB-570:
-----------------------------

             Summary: WP7 takePicture DATA_URL Not Resizing Image
                 Key: CB-570
                 URL: https://issues.apache.org/jira/browse/CB-570
             Project: Apache Callback
          Issue Type: Bug
          Components: WP7
    Affects Versions: 1.6.1
         Environment: Cordova 1.6.1, VS.NET 2010, WP7.5 emulators
            Reporter: Alan Neveu
            Assignee: Jesse MacFadyen


The code in Camera.cs that should resize the picture file before returning the 
base64 encoded string has not been completed as of 1.6.1. I wrote code that 
does this and I'll share it here so that Jesse can restructure it a bit using 
forms that match the rest of the project.  This code works great and it was 
used in basically this same format on another Silverlight app that has been in 
the WP7 Marketplace for over a year with 8,000 installs and no errors with this 
code.  Enjoy!

/*  
        Licensed under the Apache License, Version 2.0 (the "License");
        you may not use this file except in compliance with the License.
        You may obtain a copy of the License at
        
        http://www.apache.org/licenses/LICENSE-2.0
        
        Unless required by applicable law or agreed to in writing, software
        distributed under the License is distributed on an "AS IS" BASIS,
        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        See the License for the specific language governing permissions and
        limitations under the License.
*/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Collections.Generic;
using Microsoft.Phone.Tasks;
using System.Runtime.Serialization;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows.Media.Imaging;
using Microsoft.Phone;

namespace WP7CordovaClassLib.Cordova.Commands
{
    public class Camera : BaseCommand
    {

        /// <summary>
        /// Return base64 encoded string
        /// </summary>
        private const int DATA_URL = 0;

        /// <summary>
        /// Return file uri
        /// </summary>
        private const int FILE_URI = 1;

        /// <summary>
        /// Choose image from picture library
        /// </summary>
        private const int PHOTOLIBRARY = 0;

        /// <summary>
        /// Take picture from camera
        /// </summary>

        private const int CAMERA = 1;

        /// <summary>
        /// Choose image from picture library
        /// </summary>
        private const int SAVEDPHOTOALBUM = 2;

        /// <summary>
        /// Take a picture of type JPEG
        /// </summary>
        private const int JPEG = 0;

        /// <summary>
        /// Take a picture of type PNG
        /// </summary>
        private const int PNG = 1;

        /// <summary>
        /// Folder to store captured images
        /// </summary>
        private const string isoFolder = "CapturedImagesCache";

        /// <summary>
        /// Represents captureImage action options.
        /// </summary>
        [DataContract]
        public class CameraOptions
        {
            /// <summary>
            /// Source to getPicture from.
            /// </summary>
            [DataMember(IsRequired = false, Name = "sourceType")]
            public int PictureSourceType { get; set; }

            /// <summary>
            /// Format of image that returned from getPicture.
            /// </summary>
            [DataMember(IsRequired = false, Name = "destinationType")]
            public int DestinationType { get; set; }

            /// <summary>
            /// Quality of saved image
            /// </summary>
            [DataMember(IsRequired = false, Name = "quality")]
            public int Quality { get; set; }


            /// <summary>
            /// Height in pixels to scale image
            /// </summary>
            [DataMember(IsRequired = false, Name = "targetHeight")]
            public int TargetHeight { get; set; }

            /// <summary>
            /// Width in pixels to scale image
            /// </summary>
            [DataMember(IsRequired = false, Name = "targetWidth")]
            public int TargetWidth { get; set; }

            /// <summary>
            /// Creates options object with default parameters
            /// </summary>
            public CameraOptions()
            {
                this.SetDefaultValues(new StreamingContext());
            }

            /// <summary>
            /// Initializes default values for class fields.
            /// Implemented in separate method because default constructor is 
not invoked during deserialization.
            /// </summary>
            /// <param name="context"></param>
            [OnDeserializing()]
            public void SetDefaultValues(StreamingContext context)
            {
                PictureSourceType = CAMERA;
                DestinationType = FILE_URI;
                Quality = 80;
                TargetHeight = -1;
                TargetWidth = -1;
            }

        }

        /// <summary>
        /// Used to open photo library
        /// </summary>
        PhotoChooserTask photoChooserTask;

        /// <summary>
        /// Used to open camera application
        /// </summary>
        CameraCaptureTask cameraTask;

        /// <summary>
        /// Camera options
        /// </summary>
        CameraOptions cameraOptions;

        public void takePicture(string options)
        {
            try
            {
                this.cameraOptions = String.IsNullOrEmpty(options) ?
                        new CameraOptions() : 
JSON.JsonHelper.Deserialize<CameraOptions>(options);
            }
            catch (Exception ex)
            {
                this.DispatchCommandResult(new 
PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
                return;
            }

            //TODO Check if all the options are acceptable


            if (cameraOptions.PictureSourceType == CAMERA)
            {
                cameraTask = new CameraCaptureTask();
                cameraTask.Completed += onTaskCompleted;
                cameraTask.Show();
            }
            else
            {
                if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || 
(cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
                {
                    photoChooserTask = new PhotoChooserTask();
                    photoChooserTask.Completed += onTaskCompleted;
                    photoChooserTask.Show();
                }
                else
                {
                    DispatchCommandResult(new 
PluginResult(PluginResult.Status.NO_RESULT));
                }
            }

        }

        public void onTaskCompleted(object sender, PhotoResult e)
        {
            if (e.Error != null)
            {
                DispatchCommandResult(new 
PluginResult(PluginResult.Status.ERROR));
                return;
            }

            switch (e.TaskResult)
            {
                case TaskResult.OK:
                    try
                    {
                        string imagePathOrContent = string.Empty;

                        if (cameraOptions.DestinationType == FILE_URI)
                        {
                            WriteableBitmap image = 
PictureDecoder.DecodeJpeg(e.ChosenPhoto);
                            imagePathOrContent = 
this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
                        }
                        else if (cameraOptions.DestinationType == DATA_URL)
                        {
                            Byte[] bytFile;
                            Byte[] bytFileResized;
                            int intResult = 0;
                            long lngLength;
                            BitmapImage objBitmap;
                            System.IO.MemoryStream objBitmapStream;
                            System.IO.MemoryStream objBitmapStreamResized = new 
System.IO.MemoryStream();
                            WriteableBitmap objWB;

                            //Get the JPEG bytes from the source chosen photo, 
probably 5 megapixel!
                            lngLength = e.ChosenPhoto.Length;
                            bytFile = new Byte[lngLength];
                            intResult = e.ChosenPhoto.Read(bytFile, 0, 
(int)lngLength);

                            //Convert the bytes to a WriteableBitmap
                            objBitmap = new BitmapImage();
                            objBitmapStream = new 
System.IO.MemoryStream(bytFile);
                            objBitmap.SetSource(objBitmapStream);
                            objWB = new WriteableBitmap(objBitmap);

                            //Resize the image
                            
System.Windows.Media.Imaging.Extensions.SaveJpeg(objWB, objBitmapStreamResized, 
cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, 
cameraOptions.Quality);

                            //Convert the resized stream to a byte array.
                            lngLength = objBitmapStreamResized.Length;
                            bytFileResized = new Byte[lngLength]; //-1
                            objBitmapStreamResized.Position = 0; //for some 
reason we have to set Position to zero, but we don't have to earlier when we 
get the bytes from the chosen photo...
                            intResult = 
objBitmapStreamResized.Read(bytFileResized, 0, (int)lngLength);

                            //Convert the byte array to base64 string.
                            imagePathOrContent = 
Convert.ToBase64String(bytFileResized);
                        }
                        else
                        {
                            DispatchCommandResult(new 
PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
                            return;
                        }

                        DispatchCommandResult(new 
PluginResult(PluginResult.Status.OK, imagePathOrContent));

                    }
                    catch (Exception)
                    {
                        DispatchCommandResult(new 
PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
                    }
                    break;

                case TaskResult.Cancel:
                    DispatchCommandResult(new 
PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
                    break;

                default:
                    DispatchCommandResult(new 
PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
                    break;
            }
        }


        /// <summary>
        /// Returns image content in a form of base64 string
        /// </summary>
        /// <param name="stream">Image stream</param>
        /// <returns>Base64 representation of the image</returns>
        private string GetImageContent(Stream stream)
        {
            int streamLength = (int)stream.Length;
            byte[] fileData = new byte[streamLength + 1];
            stream.Read(fileData, 0, streamLength);
            stream.Close();

            return Convert.ToBase64String(fileData);
        }


        /// <summary>
        /// Saves captured image in isolated storage
        /// </summary>
        /// <param name="imageFileName">image file name</param>
        /// <returns>Image path</returns>
        private string SaveImageToLocalStorage(WriteableBitmap image, string 
imageFileName)
        {

            if (image == null)
            {
                throw new ArgumentNullException("imageBytes");
            }
            try
            {


                var isoFile = IsolatedStorageFile.GetUserStoreForApplication();

                if (!isoFile.DirectoryExists(isoFolder))
                {
                    isoFile.CreateDirectory(isoFolder);
                }

                string filePath = System.IO.Path.Combine("/" + isoFolder + "/", 
imageFileName);

                using (var stream = isoFile.CreateFile(filePath))
                {
                    // resize image if Height and Width defined via options 
                    if (cameraOptions.TargetHeight > 0 && 
cameraOptions.TargetWidth > 0)
                    {
                        image.SaveJpeg(stream, cameraOptions.TargetWidth, 
cameraOptions.TargetHeight, 0, cameraOptions.Quality);
                    }
                    else
                    {
                        image.SaveJpeg(stream, image.PixelWidth, 
image.PixelHeight, 0, cameraOptions.Quality);
                    }
                }

                return new Uri(filePath, UriKind.Relative).ToString();
            }
            catch (Exception)
            {
                //TODO: log or do something else
                throw;
            }
        }

    }
}


--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: 
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira

        

Reply via email to