1

I have a cropping tool in my website that users can upload pictures and crop it.

I am encountering whenever I upload a picture that was captured by mobile phone the images orientation is wrong.

HTML

<div class="modal-body">

            @using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "formAjax", enctype = "multipart/form-data", action = "/ProfilePicture/Crop" }))
            {
                @Html.AntiForgeryToken()

                <label> Upload Photo: </label>
                <label class="btn btn-default btn-file">
                    Browse @Html.TextBoxFor(ModelIndex => ModelIndex.ProfilePic.MyFile, new { id = "file", type = "file", style = "display: none;" })
                </label>

                <br /><br />
                <div id="crop-image-area" style="display:none;">
                    <table border="0" cellpadding="0" cellspacing="5">
                        <tr>
                            <td>
                                <label>Preview: </label>
                                <div class="img-circle" style="width:100px;height:100px;overflow:hidden;margin-left:5px;">
                                    <canvas id="preview"></canvas>
                                </div>
                                <br />
                            </td>
                        </tr>
                        <tr>
                            <td>
                                <label>Crop Here: </label>
                                <img src="" id="cropbox" style="display: none;">
                            </td>
                        </tr>
                    </table>
                    <br />

                    <input type="hidden" id="cropPointX" name="cropPointX" />
                    <input type="hidden" id="cropPointY" name="cropPointY" />
                    <input type="hidden" id="imageCropWidth" name="imageCropWidth" />
                    <input type="hidden" id="imageCropHeight" name="imageCropHeight" />
                    <span>
                        <input type="submit" id="btnCrop" class="btn btn-success" value="Crop" />
                        <label>
                            <img id="loader" src="~/Content/images/loading.gif" style="display:none;" />
                        </label>
                    </span>
                </div>

            }
</div>

JS

$(function() {
    var jcrop_api;

    $("#file").change(function() {
        var reader = new FileReader();
        reader.onload = function(e) {
            $('#cropbox').show();
            $('#cropbox').attr('src', e.target.result);
            $('#cropbox').Jcrop({
                onChange: updatePreview,
                onSelect: getcroparea,
                boxWidth: 400,
                boxHeight: 400,
                aspectRatio: 1,
                bgOpacity: .4,
                setSelect: [80, 45, 100, 100]
            }, function() {
                //first attempt
                if (jcrop_api != null) {
                    jcrop_api.destroy();
                }
                //second attempt - even this does not work
                $(".jcrop-holder").not(":last").remove();
                jcrop_api = this;
            });

            $("#crop-image-area").hide();
            $("#crop-image-area").fadeIn("slow");

        }
        reader.readAsDataURL($(this)[0].files[0]);
        if ($('#cropbox').data('Jcrop')) {
            $('#cropbox').data('Jcrop').destroy();
            $('#cropbox').removeAttr('style');
        }

    });

    function updatePreview(c) {
        if (parseInt(c.w) > 0) {
            var imageObj = jQuery("#cropbox")[0];
            var canvas = jQuery("#preview")[0];
            var context = canvas.getContext("2d");
            context.beginPath();
            context.arc(50, 50, 50, Math.PI * 4, 0, true);
            context.clip();
            context.closePath();
            context.drawImage(imageObj, c.x, c.y, c.w, c.h, 0, 0, 100, 100);
        }
    };

    function getcroparea(c) {

        imageCropWidth = c.w;
        imageCropHeight = c.h;
        cropPointX = c.x;
        cropPointY = c.y;

        $('#cropPointX').val(Math.round(cropPointX))
        $('#cropPointY').val(Math.round(cropPointY))
        $('#imageCropWidth').val(Math.round(imageCropWidth))
        $('#imageCropHeight').val(Math.round(imageCropHeight))
    }

    function destroyCrop() {
        var jcropApi = $('#cropbox').data('Jcrop');
        if (jcropApi !== undefined) {
            jcropApi.destroy();
            $('#cropbox').attr('style', "").attr("src", "");
        }
    }

    function destroyPreview() {
        $('#preview').removeAttr('src');
    }

    function cropImage() {

        $("#loader").show();
        $('#formAjax').ajaxSubmit({
            type: 'POST',
            success: function(result) {
                $("#loader").hide();
                $('#myProfilePicModal').modal('hide');
                $("#crop-image-area").fadeOut("slow");
                destroyCrop();
                destroyPreview();
                $("#alert-success").show();
                $('#newImage').attr('src', 'data:image;base64,' + result.Photo);
                $('.img-avatar').attr('src', 'data:image;base64,' + result.Avatar);
                setTimeout(function() {
                    $("#alert-success").hide();
                }, 5000);
            },
            error: function() {
                $("#loader").hide();
                $('#myProfilePicModal').modal('hide');
                $("#crop-image-area").fadeOut("slow");
                destroyCrop();
                destroyPreview();
                $("#alert-fail").show();
                setTimeout(function() {
                    $("#alert-fail").hide();
                }, 5000);
            }
        });
    }
    $("#btnCrop").on("click", function(e) {
        e.preventDefault();
        cropImage();
    });
});

I am not familiar with EXIF orientation please help me in implementing it on my code.

Krupesh Kotecha
  • 2,396
  • 3
  • 21
  • 40
Terence
  • 352
  • 1
  • 5
  • 18

1 Answers1

0

What I think you are saying is you want to extract EXIF data from the uploaded picture and then rotate the picture so it will always be in some position. For me it is rather unclear on what happens with the image, say for example I upload a picture made in landscape mode with my phone will that photo be upside down? and what happens when I upload a photo made with portret mode what will happen to this image? Because I don't know this information I can't create the full code for you but I can help you get on your way.

Here is a snippet of extracting EXIF data from an image on the client-side. This will trigger when someone adds an image to an input file. This snippet was made by the user: https://stackoverflow.com/users/893432/ali

function getOrientation(file, callback) {
  var reader = new FileReader();
  reader.onload = function(e) {

    var view = new DataView(e.target.result);
    if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
    var length = view.byteLength, offset = 2;
    while (offset < length) {
      var marker = view.getUint16(offset, false);
      offset += 2;
      if (marker == 0xFFE1) {
        if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1);
        var little = view.getUint16(offset += 6, false) == 0x4949;
        offset += view.getUint32(offset + 4, little);
        var tags = view.getUint16(offset, little);
        offset += 2;
        for (var i = 0; i < tags; i++)
          if (view.getUint16(offset + (i * 12), little) == 0x0112)
            return callback(view.getUint16(offset + (i * 12) + 8, little));
      }
      else if ((marker & 0xFF00) != 0xFF00) break;
      else offset += view.getUint16(offset, false);
    }
    return callback(-1);
  };
  reader.readAsArrayBuffer(file);
}

// usage:
var input = document.getElementById('input');
input.onchange = function(e) {
  getOrientation(input.files[0], function(orientation) {
    alert('orientation: ' + orientation);
  });
}
<input id='input' type='file' />

This snippet will extract EXIF data from a picture if it contains EXIF data, the response values you can get are these:

-2: not jpeg
-1: not defined

So when it is not a jpeg you will get -2 when there is no picture at all you get -1. If it worked you will get one of the responses in the image and you have to rotate the image with jcrop.

enter image description here

So lets say you got the code 8 and the image is turned 45 degrees to the right, you can use this code to rotate it 45 degrees to the left so you know it is upright.

var jcp = $("#input").Jcrop('api');
jcp.ui.stage.setAngle(45).redraw();

As far as I know this snippet only works with jCrop 2+, You can also try to do this:

$('#cropbox').Jcrop({
        onChange: updatePreview,
        onSelect: getcroparea,
        boxWidth: 400,
        boxHeight: 400,
        setAngle: 45,
        aspectRatio: 1,
        bgOpacity: .4,
        setSelect: [80, 45, 100, 100]
    }

But I have not tested the latter .

Your best bet is to:

  1. use the snippet I provided that extracts the rotation of the image, based on the number you get back calculate how much you will need to turn the image/flip the image
  2. then rebuild your jCrop solution with jCrop version 2.0 and use the 2 line snippet I provided to rotate your image.

Here is another solution which requires you too change the jCrop code a bit to allow for rotation. Make Jcrop tracker not rotate when cropping a rotated image

Community
  • 1
  • 1
Gertjan Brouwer
  • 996
  • 1
  • 12
  • 35
  • Hi, @gertjan thanks for answering. sorry if my question is unclear to you. what i want to happen is whenever a user uploads an image the displayed image on the canvas will have a correct orientation. cause as of now it outputs a wrong orientation of `8` (based on the diagram above). – Terence Mar 15 '17 at 07:50
  • Hi @Terence , this means you have to turn the image 270 degrees with setAngle or use this redraw function also described in my answer jcp.ui.stage.setAngle(45).redraw(); – Gertjan Brouwer Mar 16 '17 at 09:44