zoukankan      html  css  js  c++  java
  • angular.js 老版本实现图片裁剪-4:3比例

    1.本人声明是在别人的一个老文章上面修改的。

    2.原文 https://github.com/alexk111/ngImgCrop

    3.我的使用

    /*!
    * ngImgCrop liugongliang yuanchuang
    * ngImgCrop xinyifangwu
    *
    * Copyright (c) 2020
    *
    *
    * Generated at liugongliang, December 3rd, 2020, 17:59:12 PM
    */
    (function() {
    'use strict';
    var crop = angular.module('ngImgCrop', []);
    crop.factory('cropAreaCircle', ['cropArea', function (CropArea) {
    var CropAreaCircle = function() {
    CropArea.apply(this, arguments);

    this._boxResizeBaseSize = 500;
    this._boxResizeNormalRatio = 0.9;
    this._boxResizeHoverRatio = 1.2;
    this._iconMoveNormalRatio = 0.9;
    this._iconMoveHoverRatio = 1.2;

    this._boxResizeNormalSize = this._boxResizeBaseSize*this._boxResizeNormalRatio;
    this._boxResizeHoverSize = this._boxResizeBaseSize*this._boxResizeHoverRatio;

    this._posDragStartX=0;
    this._posDragStartY=0;
    this._posResizeStartX=0;
    this._posResizeStartY=0;
    this._posResizeStartSize=500;

    this._boxResizeIsHover = false;
    this._areaIsHover = false;
    this._boxResizeIsDragging = false;
    this._areaIsDragging = false;
    };

    CropAreaCircle.prototype = new CropArea();

    CropAreaCircle.prototype._calcCirclePerimeterCoords=function(angleDegrees) {
    var hSize=this._size/2;
    var angleRadians=angleDegrees * (Math.PI / 180),
    circlePerimeterX=this._x + hSize * Math.cos(angleRadians),
    circlePerimeterY=this._y + hSize * Math.sin(angleRadians);
    return [circlePerimeterX, circlePerimeterY];
    };

    CropAreaCircle.prototype._calcResizeIconCenterCoords=function() {
    return this._calcCirclePerimeterCoords(-45);
    };

    CropAreaCircle.prototype._isCoordWithinArea=function(coord) {
    return Math.sqrt((coord[0]-this._x)*(coord[0]-this._x) + (coord[1]-this._y)*(coord[1]-this._y)) < this._size/2;
    };
    CropAreaCircle.prototype._isCoordWithinBoxResize=function(coord) {
    var resizeIconCenterCoords=this._calcResizeIconCenterCoords();
    var hSize=this._boxResizeHoverSize/2;
    return(coord[0] > resizeIconCenterCoords[0] - hSize && coord[0] < resizeIconCenterCoords[0] + hSize &&
    coord[1] > resizeIconCenterCoords[1] - hSize && coord[1] < resizeIconCenterCoords[1] + hSize);
    };
    CropAreaCircle.prototype._drawArea=function(ctx,centerCoords,size){
    ctx.arc(centerCoords[0],centerCoords[1],size/2,0,2*Math.PI);
    };

    CropAreaCircle.prototype.draw=function() {
    CropArea.prototype.draw.apply(this, arguments);

    // draw move icon
    this._cropCanvas.drawIconMove([this._x,this._y*3/4], this._areaIsHover?this._iconMoveHoverRatio:this._iconMoveNormalRatio);

    // draw resize cubes
    this._cropCanvas.drawIconResizeBoxNESW(this._calcResizeIconCenterCoords(), this._boxResizeBaseSize, this._boxResizeIsHover?this._boxResizeHoverRatio:this._boxResizeNormalRatio);
    };

    CropAreaCircle.prototype.processMouseMove=function(mouseCurX, mouseCurY) {
    var cursor='default';
    var res=false;

    this._boxResizeIsHover = false;
    this._areaIsHover = false;

    if (this._areaIsDragging) {
    this._x = mouseCurX - this._posDragStartX;
    this._y = mouseCurY - this._posDragStartY;
    this._areaIsHover = true;
    cursor='move';
    res=true;
    this._events.trigger('area-move');
    } else if (this._boxResizeIsDragging) {
    cursor = 'nesw-resize';
    var iFR, iFX, iFY;
    iFX = mouseCurX - this._posResizeStartX;
    iFY = this._posResizeStartY - mouseCurY;
    if(iFX>iFY) {
    iFR = this._posResizeStartSize + iFY*2;
    } else {
    iFR = this._posResizeStartSize + iFX*2;
    }
    this._size = Math.max(this._minSize, iFR);
    // this._size =iFR;
    this._boxResizeIsHover = true;
    res=true;
    this._events.trigger('area-resize');
    } else if (this._isCoordWithinBoxResize([mouseCurX,mouseCurY])) {
    cursor = 'nesw-resize';
    this._areaIsHover = false;
    this._boxResizeIsHover = true;
    res=true;
    } else if(this._isCoordWithinArea([mouseCurX,mouseCurY])) {
    cursor = 'move';
    this._areaIsHover = true;
    res=true;
    }

    this._dontDragOutside();
    angular.element(this._ctx.canvas).css({'cursor': cursor});

    return res;
    };
    CropAreaCircle.prototype.processMouseDown=function(mouseDownX, mouseDownY) {
    if (this._isCoordWithinBoxResize([mouseDownX,mouseDownY])) {
    this._areaIsDragging = false;
    this._areaIsHover = false;
    this._boxResizeIsDragging = true;
    this._boxResizeIsHover = true;
    this._posResizeStartX=mouseDownX;
    this._posResizeStartY=mouseDownY;
    this._posResizeStartSize = this._size;
    this._events.trigger('area-resize-start');
    } else if (this._isCoordWithinArea([mouseDownX,mouseDownY])) {
    this._areaIsDragging = true;
    this._areaIsHover = true;
    this._boxResizeIsDragging = false;
    this._boxResizeIsHover = false;
    this._posDragStartX = mouseDownX - this._x;
    this._posDragStartY = mouseDownY - this._y;
    this._events.trigger('area-move-start');
    }
    };

    CropAreaCircle.prototype.processMouseUp=function(/*mouseUpX, mouseUpY*/) {
    if(this._areaIsDragging) {
    this._areaIsDragging = false;
    this._events.trigger('area-move-end');
    }
    if(this._boxResizeIsDragging) {
    this._boxResizeIsDragging = false;
    this._events.trigger('area-resize-end');
    }
    this._areaIsHover = false;
    this._boxResizeIsHover = false;

    this._posDragStartX = 0;
    this._posDragStartY = 0;
    };

    return CropAreaCircle;
    }]);

    crop.factory('cropAreaSquare', ['cropArea', function(CropArea) {
    var CropAreaSquare = function() {
    CropArea.apply(this, arguments);

    this._resizeCtrlBaseRadius = 10;
    this._resizeCtrlNormalRatio = 0.75;
    this._resizeCtrlHoverRatio = 1;
    this._iconMoveNormalRatio = 0.9;
    this._iconMoveHoverRatio = 1.2;

    this._resizeCtrlNormalRadius = this._resizeCtrlBaseRadius*this._resizeCtrlNormalRatio;
    this._resizeCtrlHoverRadius = this._resizeCtrlBaseRadius*this._resizeCtrlHoverRatio;

    this._posDragStartX=0;
    this._posDragStartY=0;
    this._posResizeStartX=0;
    this._posResizeStartY=0 ;
    this._posResizeStartSize=1000;

    this._resizeCtrlIsHover = -1;
    this._areaIsHover = false;
    this._resizeCtrlIsDragging = -1;
    this._areaIsDragging = false;
    };

    CropAreaSquare.prototype = new CropArea();

    CropAreaSquare.prototype._calcSquareCorners=function() {
    var hSize = this._size / 2;
    var ySize = hSize * 3 / 4;
    return [
    [this._x - hSize, this._y - ySize],
    [this._x + hSize, this._y - ySize],
    [this._x - hSize, this._y + ySize],
    [this._x + hSize, this._y + ySize]
    ];
    };

    CropAreaSquare.prototype._calcSquareDimensions=function() {
    var hSize = this._size / 2;
    var ySize = hSize * 3 / 4;
    return {
    left: this._x-hSize,
    top: this._y - ySize,
    right: this._x+hSize,
    bottom: this._y + ySize
    };
    };

    CropAreaSquare.prototype._isCoordWithinArea=function(coord) {
    var squareDimensions=this._calcSquareDimensions();
    return (coord[0]>=squareDimensions.left&&coord[0]<=squareDimensions.right&&coord[1]>=squareDimensions.top&&coord[1]<=squareDimensions.bottom);
    };

    CropAreaSquare.prototype._isCoordWithinResizeCtrl=function(coord) {
    var resizeIconsCenterCoords=this._calcSquareCorners();
    var res=-1;
    for(var i=0,len=resizeIconsCenterCoords.length;i<len;i++) {
    var resizeIconCenterCoords=resizeIconsCenterCoords[i];
    if(coord[0] > resizeIconCenterCoords[0] - this._resizeCtrlHoverRadius && coord[0] < resizeIconCenterCoords[0] + this._resizeCtrlHoverRadius &&
    coord[1] > resizeIconCenterCoords[1] - this._resizeCtrlHoverRadius && coord[1] < resizeIconCenterCoords[1] + this._resizeCtrlHoverRadius) {
    res=i;
    break;
    }
    }
    return res;
    };

    CropAreaSquare.prototype._drawArea=function(ctx,centerCoords,size){
    var hSize = size / 2;
    var Sizeh = size * 3 / 4;
    ctx.rect(centerCoords[0] - hSize, centerCoords[1] - hSize * 3 / 4, size, Sizeh);
    ctx.rect(centerCoords[0] - hSize, centerCoords[1] - hSize * 3 / 4, size, Sizeh);
    };
    //一直加载
    CropAreaSquare.prototype.draw = function () {
    CropArea.prototype.draw.apply(this, arguments);
    // draw move icon
    this._cropCanvas.drawIconMove([this._x,this._y], this._areaIsHover?this._iconMoveHoverRatio:this._iconMoveNormalRatio);
    // draw resize cubes
    var resizeIconsCenterCoords=this._calcSquareCorners();
    for(var i=0,len=resizeIconsCenterCoords.length;i<len;i++) {
    var resizeIconCenterCoords=resizeIconsCenterCoords[i];
    this._cropCanvas.drawIconResizeCircle(resizeIconCenterCoords, this._resizeCtrlBaseRadius, this._resizeCtrlIsHover===i?this._resizeCtrlHoverRatio:this._resizeCtrlNormalRatio);
    }
    };
    //鼠标移动事件
    CropAreaSquare.prototype.processMouseMove = function (mouseCurX, mouseCurY) {
    var cursor='default';
    var res=false;

    this._resizeCtrlIsHover = -1;
    this._areaIsHover = false;

    if (this._areaIsDragging) {
    this._x = mouseCurX - this._posDragStartX;
    this._y = mouseCurY - this._posDragStartY;
    this._areaIsHover = true;
    cursor='move';
    res=true;
    this._events.trigger('area-move');
    } else if (this._resizeCtrlIsDragging>-1) {
    var xMulti, yMulti;
    switch(this._resizeCtrlIsDragging) {
    case 0: // Top Left
    xMulti=-1;
    yMulti=-1;
    cursor = 'nwse-resize';
    break;
    case 1: // Top Right
    xMulti=1;
    yMulti=-1;
    cursor = 'nesw-resize';
    break;
    case 2: // Bottom Left
    xMulti=-1;
    yMulti=1;
    cursor = 'nesw-resize';
    break;
    case 3: // Bottom Right
    xMulti=1;
    yMulti=1;
    cursor = 'nwse-resize';
    break;
    }
    var iFX = (mouseCurX - this._posResizeStartX)*xMulti;
    var iFY = (mouseCurY - this._posResizeStartY)*yMulti;
    var iFR;
    if(iFX>iFY) {
    iFR = this._posResizeStartSize + iFY;
    } else {
    iFR = this._posResizeStartSize + iFX;
    }
    var wasSize = this._size;
    this._size = Math.max(this._minSize, iFR);
    var posModifier=(this._size-wasSize)/2;
    this._x+=posModifier*xMulti;
    this._y+=posModifier*yMulti;
    this._resizeCtrlIsHover = this._resizeCtrlIsDragging;
    res=true;
    this._events.trigger('area-resize');
    } else {
    var hoveredResizeBox=this._isCoordWithinResizeCtrl([mouseCurX,mouseCurY]);
    if (hoveredResizeBox>-1) {
    switch(hoveredResizeBox) {
    case 0:
    cursor = 'nwse-resize';
    break;
    case 1:
    cursor = 'nesw-resize';
    break;
    case 2:
    cursor = 'nesw-resize';
    break;
    case 3:
    cursor = 'nwse-resize';
    break;
    }
    this._areaIsHover = false;
    this._resizeCtrlIsHover = hoveredResizeBox;
    res=true;
    } else if(this._isCoordWithinArea([mouseCurX,mouseCurY])) {
    cursor = 'move';
    this._areaIsHover = true;
    res=true;
    }
    }

    this._dontDragOutside();
    angular.element(this._ctx.canvas).css({'cursor': cursor});

    return res;
    };

    CropAreaSquare.prototype.processMouseDown=function(mouseDownX, mouseDownY) {
    var isWithinResizeCtrl=this._isCoordWithinResizeCtrl([mouseDownX,mouseDownY]);
    if (isWithinResizeCtrl>-1) {
    this._areaIsDragging = false;
    this._areaIsHover = false;
    this._resizeCtrlIsDragging = isWithinResizeCtrl;
    this._resizeCtrlIsHover = isWithinResizeCtrl;
    this._posResizeStartX=mouseDownX;
    this._posResizeStartY=mouseDownY;
    this._posResizeStartSize = this._size;
    this._events.trigger('area-resize-start');
    } else if (this._isCoordWithinArea([mouseDownX,mouseDownY])) {
    this._areaIsDragging = true;
    this._areaIsHover = true;
    this._resizeCtrlIsDragging = -1;
    this._resizeCtrlIsHover = -1;
    this._posDragStartX = mouseDownX - this._x;
    this._posDragStartY = mouseDownY - this._y;
    this._events.trigger('area-move-start');
    }
    };

    CropAreaSquare.prototype.processMouseUp=function(/*mouseUpX, mouseUpY*/) {
    if(this._areaIsDragging) {
    this._areaIsDragging = false;
    this._events.trigger('area-move-end');
    }
    if(this._resizeCtrlIsDragging>-1) {
    this._resizeCtrlIsDragging = -1;
    this._events.trigger('area-resize-end');
    }
    this._areaIsHover = false;
    this._resizeCtrlIsHover = -1;

    this._posDragStartX = 0;
    this._posDragStartY = 0;
    };

    return CropAreaSquare;
    }]);

    crop.factory('cropArea', ['cropCanvas', function(CropCanvas) {
    var CropArea = function(ctx, events) {
    this._ctx=ctx;
    this._events=events;

    this._minSize=500;

    this._cropCanvas=new CropCanvas(ctx);

    this._image=new Image();
    this._x = 0;
    this._y = 0;
    this._size = 500;
    };

    /* GETTERS/SETTERS */

    CropArea.prototype.getImage = function () {
    return this._image;
    };
    CropArea.prototype.setImage = function (image) {
    this._image = image;
    };

    CropArea.prototype.getX = function () {
    return this._x;
    };
    CropArea.prototype.setX = function (x) {
    this._x = x;
    this._dontDragOutside();
    };

    CropArea.prototype.getY = function () {
    return this._y;
    };
    CropArea.prototype.setY = function (y) {
    this._y = y;
    this._dontDragOutside();
    };

    CropArea.prototype.getSize = function () {
    return this._size;
    };
    CropArea.prototype.setSize = function (size) {
    this._size = Math.max(this._minSize, size);
    this._dontDragOutside();
    };

    CropArea.prototype.getMinSize = function () {
    return this._minSize;
    };
    CropArea.prototype.setMinSize = function (size) {
    this._minSize = 0;
    this._size = size;
    this._size = Math.max(this._minSize, this._size);
    this._dontDragOutside();
    };

    /* FUNCTIONS */
    //定义不要拖到外面的尺寸
    CropArea.prototype._dontDragOutside=function() {
    var h=this._ctx.canvas.height,
    w = this._ctx.canvas.width;
    if (w >= h) {//宽大于高
    var h1 = h;
    h = h * 4 / 3;
    if(this._size>w) { this._size=w; }
    if (this._size > h) { this._size = h; }
    if(this._x<this._size/2) { this._x=this._size/2; }
    if (this._x > w - this._size/2) { this._x = w - this._size/2;}
    if(this._y<this._size*3/8) { this._y=this._size*3/8;}
    if (this._y > h1 - this._size * 3/8) { this._y = h1 - this._size*3/8; }//这里要修改尺寸
    }else{//高大于宽
    if(this._size>w) { this._size=w; }
    if(this._size>h) { this._size=h; }
    if(this._x<this._size/2) { this._x=this._size/2; }
    if(this._x>w-this._size/2) { this._x=w-this._size/2; }
    if(this._y<this._size*3/8) { this._y=this._size*3/8; }
    if (this._y > h - this._size *3/ 8) { this._y = h - this._size * 3 / 8; }//这里要修改尺寸
    }
    };

    CropArea.prototype._drawArea=function() {};

    CropArea.prototype.draw=function() {
    // draw crop area
    this._cropCanvas.drawCropArea(this._image,[this._x,this._y],this._size,this._drawArea);
    };

    CropArea.prototype.processMouseMove=function() {};

    CropArea.prototype.processMouseDown=function() {};

    CropArea.prototype.processMouseUp=function() {};

    return CropArea;
    }]);

    crop.factory('cropCanvas', [function() {
    // Shape = Array of [x,y]; [0, 0] - center
    var shapeArrowNW=[[-0.5,-2],[-3,-4.5],[-0.5,-7],[-7,-7],[-7,-0.5],[-4.5,-3],[-2,-0.5]];
    var shapeArrowNE=[[0.5,-2],[3,-4.5],[0.5,-7],[7,-7],[7,-0.5],[4.5,-3],[2,-0.5]];
    var shapeArrowSW=[[-0.5,2],[-3,4.5],[-0.5,7],[-7,7],[-7,0.5],[-4.5,3],[-2,0.5]];
    var shapeArrowSE=[[0.5,2],[3,4.5],[0.5,7],[7,7],[7,0.5],[4.5,3],[2,0.5]];
    var shapeArrowN=[[-1.5,-2.5],[-1.5,-6],[-5,-6],[0,-11],[5,-6],[1.5,-6],[1.5,-2.5]];
    var shapeArrowW=[[-2.5,-1.5],[-6,-1.5],[-6,-5],[-11,0],[-6,5],[-6,1.5],[-2.5,1.5]];
    var shapeArrowS=[[-1.5,2.5],[-1.5,6],[-5,6],[0,11],[5,6],[1.5,6],[1.5,2.5]];
    var shapeArrowE=[[2.5,-1.5],[6,-1.5],[6,-5],[11,0],[6,5],[6,1.5],[2.5,1.5]];

    // Colors
    var colors={
    areaOutline: '#fff',
    resizeBoxStroke: '#fff',
    resizeBoxFill: '#444',
    resizeBoxArrowFill: '#fff',
    resizeCircleStroke: '#fff',
    resizeCircleFill: '#444',
    moveIconFill: '#fff'
    };

    return function(ctx){

    /* Base functions */

    // Calculate Point
    var calcPoint=function(point,offset,scale) {
    return [scale*point[0]+offset[0], scale*point[1]+offset[1]];
    };

    // 画填充多边形
    var drawFilledPolygon = function (shape, fillStyle, centerCoords, scale) {
    ctx.save();
    ctx.fillStyle = fillStyle;
    ctx.beginPath();
    var pc, pc0=calcPoint(shape[0],centerCoords,scale);
    ctx.moveTo(pc0[0],pc0[1]);

    for(var p in shape) {
    if (p > 0) {
    pc=calcPoint(shape[p],centerCoords,scale);
    ctx.lineTo(pc[0],pc[1]);
    }
    }

    ctx.lineTo(pc0[0],pc0[1]);
    ctx.fill();
    ctx.closePath();
    ctx.restore();
    };

    /* Icons */

    this.drawIconMove = function (centerCoords, scale) {
    drawFilledPolygon(shapeArrowN, colors.moveIconFill, centerCoords, scale);
    drawFilledPolygon(shapeArrowW, colors.moveIconFill, centerCoords, scale);
    drawFilledPolygon(shapeArrowS, colors.moveIconFill, centerCoords, scale);
    drawFilledPolygon(shapeArrowE, colors.moveIconFill, centerCoords, scale);
    };

    this.drawIconResizeCircle=function(centerCoords, circleRadius, scale) {
    var scaledCircleRadius=circleRadius*scale;
    ctx.save();
    ctx.strokeStyle = colors.resizeCircleStroke;
    ctx.lineWidth = 2;
    ctx.fillStyle = colors.resizeCircleFill;
    ctx.beginPath();
    ctx.arc(centerCoords[0],centerCoords[1],scaledCircleRadius,0,2*Math.PI);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
    ctx.restore();
    };

    this.drawIconResizeBoxBase = function (centerCoords, boxSize, scale) {
    var scaledBoxSize=boxSize*scale;
    ctx.save();
    ctx.strokeStyle = colors.resizeBoxStroke;
    ctx.lineWidth = 2;
    ctx.fillStyle = colors.resizeBoxFill;
    ctx.fillRect(centerCoords[0] - scaledBoxSize/2, centerCoords[1] - scaledBoxSize/2, scaledBoxSize, scaledBoxSize*3/4);
    ctx.strokeRect(centerCoords[0] - scaledBoxSize / 2, centerCoords[1] - scaledBoxSize / 2, scaledBoxSize, scaledBoxSize*3/4);
    ctx.restore();
    };
    this.drawIconResizeBoxNESW=function(centerCoords, boxSize, scale) {
    this.drawIconResizeBoxBase(centerCoords, boxSize, scale);
    drawFilledPolygon(shapeArrowNE, colors.resizeBoxArrowFill, centerCoords, scale);
    drawFilledPolygon(shapeArrowSW, colors.resizeBoxArrowFill, centerCoords, scale);
    };
    this.drawIconResizeBoxNWSE=function(centerCoords, boxSize, scale) {
    this.drawIconResizeBoxBase(centerCoords, boxSize, scale);
    drawFilledPolygon(shapeArrowNW, colors.resizeBoxArrowFill, centerCoords, scale);
    drawFilledPolygon(shapeArrowSE, colors.resizeBoxArrowFill, centerCoords, scale);
    };

    /* Crop Area */

    this.drawCropArea=function(image, centerCoords, size, fnDrawClipPath) {
    var xRatio=image.width/ctx.canvas.width,
    yRatio=image.height/ctx.canvas.height,
    xLeft=centerCoords[0]-size/2,
    yTop=centerCoords[1]-size/2;

    ctx.save();
    ctx.strokeStyle = colors.areaOutline;
    ctx.lineWidth = 2;
    ctx.beginPath();
    fnDrawClipPath(ctx, centerCoords, size);
    ctx.stroke();
    ctx.clip();

    // draw part of original image
    if (size > 0) {
    ctx.drawImage(image, xLeft*xRatio, yTop*yRatio, size*xRatio, size*yRatio, xLeft, yTop, size, size);
    }

    ctx.beginPath();
    fnDrawClipPath(ctx, centerCoords, size);
    ctx.stroke();
    ctx.clip();

    ctx.restore();
    // cropHost.setAreaMinSize(xRatio);
    };

    };
    }]);

    /**
    * EXIF service
    */

    crop.service('cropEXIF', [function() {
    var debug = false;

    var ExifTags = this.Tags = {

    // version tags
    0x9000 : "ExifVersion", // EXIF version
    0xA000 : "FlashpixVersion", // Flashpix format version

    // colorspace tags
    0xA001 : "ColorSpace", // Color space information tag

    // image configuration
    0xA002 : "PixelXDimension", // Valid width of meaningful image
    0xA003 : "PixelYDimension", // Valid height of meaningful image
    0x9101 : "ComponentsConfiguration", // Information about channels
    0x9102 : "CompressedBitsPerPixel", // Compressed bits per pixel

    // user information
    0x927C : "MakerNote", // Any desired information written by the manufacturer
    0x9286 : "UserComment", // Comments by user

    // related file
    0xA004 : "RelatedSoundFile", // Name of related sound file

    // date and time
    0x9003 : "DateTimeOriginal", // Date and time when the original image was generated
    0x9004 : "DateTimeDigitized", // Date and time when the image was stored digitally
    0x9290 : "SubsecTime", // Fractions of seconds for DateTime
    0x9291 : "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
    0x9292 : "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized

    // picture-taking conditions
    0x829A : "ExposureTime", // Exposure time (in seconds)
    0x829D : "FNumber", // F number
    0x8822 : "ExposureProgram", // Exposure program
    0x8824 : "SpectralSensitivity", // Spectral sensitivity
    0x8827 : "ISOSpeedRatings", // ISO speed rating
    0x8828 : "OECF", // Optoelectric conversion factor
    0x9201 : "ShutterSpeedValue", // Shutter speed
    0x9202 : "ApertureValue", // Lens aperture
    0x9203 : "BrightnessValue", // Value of brightness
    0x9204 : "ExposureBias", // Exposure bias
    0x9205 : "MaxApertureValue", // Smallest F number of lens
    0x9206 : "SubjectDistance", // Distance to subject in meters
    0x9207 : "MeteringMode", // Metering mode
    0x9208 : "LightSource", // Kind of light source
    0x9209 : "Flash", // Flash status
    0x9214 : "SubjectArea", // Location and area of main subject
    0x920A : "FocalLength", // Focal length of the lens in mm
    0xA20B : "FlashEnergy", // Strobe energy in BCPS
    0xA20C : "SpatialFrequencyResponse", //
    0xA20E : "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
    0xA20F : "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
    0xA210 : "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
    0xA214 : "SubjectLocation", // Location of subject in image
    0xA215 : "ExposureIndex", // Exposure index selected on camera
    0xA217 : "SensingMethod", // Image sensor type
    0xA300 : "FileSource", // Image source (3 == DSC)
    0xA301 : "SceneType", // Scene type (1 == directly photographed)
    0xA302 : "CFAPattern", // Color filter array geometric pattern
    0xA401 : "CustomRendered", // Special processing
    0xA402 : "ExposureMode", // Exposure mode
    0xA403 : "WhiteBalance", // 1 = auto white balance, 2 = manual
    0xA404 : "DigitalZoomRation", // Digital zoom ratio
    0xA405 : "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
    0xA406 : "SceneCaptureType", // Type of scene
    0xA407 : "GainControl", // Degree of overall image gain adjustment
    0xA408 : "Contrast", // Direction of contrast processing applied by camera
    0xA409 : "Saturation", // Direction of saturation processing applied by camera
    0xA40A : "Sharpness", // Direction of sharpness processing applied by camera
    0xA40B : "DeviceSettingDescription", //
    0xA40C : "SubjectDistanceRange", // Distance to subject

    // other tags
    0xA005 : "InteroperabilityIFDPointer",
    0xA420 : "ImageUniqueID" // Identifier assigned uniquely to each image
    };

    var TiffTags = this.TiffTags = {
    0x0100 : "ImageWidth",
    0x0101 : "ImageHeight",
    0x8769 : "ExifIFDPointer",
    0x8825 : "GPSInfoIFDPointer",
    0xA005 : "InteroperabilityIFDPointer",
    0x0102 : "BitsPerSample",
    0x0103 : "Compression",
    0x0106 : "PhotometricInterpretation",
    0x0112 : "Orientation",
    0x0115 : "SamplesPerPixel",
    0x011C : "PlanarConfiguration",
    0x0212 : "YCbCrSubSampling",
    0x0213 : "YCbCrPositioning",
    0x011A : "XResolution",
    0x011B : "YResolution",
    0x0128 : "ResolutionUnit",
    0x0111 : "StripOffsets",
    0x0116 : "RowsPerStrip",
    0x0117 : "StripByteCounts",
    0x0201 : "JPEGInterchangeFormat",
    0x0202 : "JPEGInterchangeFormatLength",
    0x012D : "TransferFunction",
    0x013E : "WhitePoint",
    0x013F : "PrimaryChromaticities",
    0x0211 : "YCbCrCoefficients",
    0x0214 : "ReferenceBlackWhite",
    0x0132 : "DateTime",
    0x010E : "ImageDescription",
    0x010F : "Make",
    0x0110 : "Model",
    0x0131 : "Software",
    0x013B : "Artist",
    0x8298 : "Copyright"
    };

    var GPSTags = this.GPSTags = {
    0x0000 : "GPSVersionID",
    0x0001 : "GPSLatitudeRef",
    0x0002 : "GPSLatitude",
    0x0003 : "GPSLongitudeRef",
    0x0004 : "GPSLongitude",
    0x0005 : "GPSAltitudeRef",
    0x0006 : "GPSAltitude",
    0x0007 : "GPSTimeStamp",
    0x0008 : "GPSSatellites",
    0x0009 : "GPSStatus",
    0x000A : "GPSMeasureMode",
    0x000B : "GPSDOP",
    0x000C : "GPSSpeedRef",
    0x000D : "GPSSpeed",
    0x000E : "GPSTrackRef",
    0x000F : "GPSTrack",
    0x0010 : "GPSImgDirectionRef",
    0x0011 : "GPSImgDirection",
    0x0012 : "GPSMapDatum",
    0x0013 : "GPSDestLatitudeRef",
    0x0014 : "GPSDestLatitude",
    0x0015 : "GPSDestLongitudeRef",
    0x0016 : "GPSDestLongitude",
    0x0017 : "GPSDestBearingRef",
    0x0018 : "GPSDestBearing",
    0x0019 : "GPSDestDistanceRef",
    0x001A : "GPSDestDistance",
    0x001B : "GPSProcessingMethod",
    0x001C : "GPSAreaInformation",
    0x001D : "GPSDateStamp",
    0x001E : "GPSDifferential"
    };

    var StringValues = this.StringValues = {
    ExposureProgram : {
    0 : "Not defined",
    1 : "Manual",
    2 : "Normal program",
    3 : "Aperture priority",
    4 : "Shutter priority",
    5 : "Creative program",
    6 : "Action program",
    7 : "Portrait mode",
    8 : "Landscape mode"
    },
    MeteringMode : {
    0 : "Unknown",
    1 : "Average",
    2 : "CenterWeightedAverage",
    3 : "Spot",
    4 : "MultiSpot",
    5 : "Pattern",
    6 : "Partial",
    255 : "Other"
    },
    LightSource : {
    0 : "Unknown",
    1 : "Daylight",
    2 : "Fluorescent",
    3 : "Tungsten (incandescent light)",
    4 : "Flash",
    9 : "Fine weather",
    10 : "Cloudy weather",
    11 : "Shade",
    12 : "Daylight fluorescent (D 5700 - 7100K)",
    13 : "Day white fluorescent (N 4600 - 5400K)",
    14 : "Cool white fluorescent (W 3900 - 4500K)",
    15 : "White fluorescent (WW 3200 - 3700K)",
    17 : "Standard light A",
    18 : "Standard light B",
    19 : "Standard light C",
    20 : "D55",
    21 : "D65",
    22 : "D75",
    23 : "D50",
    24 : "ISO studio tungsten",
    255 : "Other"
    },
    Flash : {
    0x0000 : "Flash did not fire",
    0x0001 : "Flash fired",
    0x0005 : "Strobe return light not detected",
    0x0007 : "Strobe return light detected",
    0x0009 : "Flash fired, compulsory flash mode",
    0x000D : "Flash fired, compulsory flash mode, return light not detected",
    0x000F : "Flash fired, compulsory flash mode, return light detected",
    0x0010 : "Flash did not fire, compulsory flash mode",
    0x0018 : "Flash did not fire, auto mode",
    0x0019 : "Flash fired, auto mode",
    0x001D : "Flash fired, auto mode, return light not detected",
    0x001F : "Flash fired, auto mode, return light detected",
    0x0020 : "No flash function",
    0x0041 : "Flash fired, red-eye reduction mode",
    0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
    0x0047 : "Flash fired, red-eye reduction mode, return light detected",
    0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
    0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
    0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
    0x0059 : "Flash fired, auto mode, red-eye reduction mode",
    0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
    0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
    },
    SensingMethod : {
    1 : "Not defined",
    2 : "One-chip color area sensor",
    3 : "Two-chip color area sensor",
    4 : "Three-chip color area sensor",
    5 : "Color sequential area sensor",
    7 : "Trilinear sensor",
    8 : "Color sequential linear sensor"
    },
    SceneCaptureType : {
    0 : "Standard",
    1 : "Landscape",
    2 : "Portrait",
    3 : "Night scene"
    },
    SceneType : {
    1 : "Directly photographed"
    },
    CustomRendered : {
    0 : "Normal process",
    1 : "Custom process"
    },
    WhiteBalance : {
    0 : "Auto white balance",
    1 : "Manual white balance"
    },
    GainControl : {
    0 : "None",
    1 : "Low gain up",
    2 : "High gain up",
    3 : "Low gain down",
    4 : "High gain down"
    },
    Contrast : {
    0 : "Normal",
    1 : "Soft",
    2 : "Hard"
    },
    Saturation : {
    0 : "Normal",
    1 : "Low saturation",
    2 : "High saturation"
    },
    Sharpness : {
    0 : "Normal",
    1 : "Soft",
    2 : "Hard"
    },
    SubjectDistanceRange : {
    0 : "Unknown",
    1 : "Macro",
    2 : "Close view",
    3 : "Distant view"
    },
    FileSource : {
    3 : "DSC"
    },

    Components : {
    0 : "",
    1 : "Y",
    2 : "Cb",
    3 : "Cr",
    4 : "R",
    5 : "G",
    6 : "B"
    }
    };

    function addEvent(element, event, handler) {
    if (element.addEventListener) {
    element.addEventListener(event, handler, false);
    } else if (element.attachEvent) {
    element.attachEvent("on" + event, handler);
    }
    }

    function imageHasData(img) {
    return !!(img.exifdata);
    }

    function base64ToArrayBuffer(base64, contentType) {
    contentType = contentType || base64.match(/^data:([^;]+);base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
    base64 = base64.replace(/^data:([^;]+);base64,/gmi, '');
    var binary = atob(base64);
    var len = binary.length;
    var buffer = new ArrayBuffer(len);
    var view = new Uint8Array(buffer);
    for (var i = 0; i < len; i++) {
    view[i] = binary.charCodeAt(i);
    }
    return buffer;
    }

    function objectURLToBlob(url, callback) {
    var http = new XMLHttpRequest();
    http.open("GET", url, true);
    http.responseType = "blob";
    http.onload = function(e) {
    if (this.status == 200 || this.status === 0) {
    callback(this.response);
    }
    };
    http.send();
    }

    function getImageData(img, callback) {
    function handleBinaryFile(binFile) {
    var data = findEXIFinJPEG(binFile);
    var iptcdata = findIPTCinJPEG(binFile);
    img.exifdata = data || {};
    img.iptcdata = iptcdata || {};
    if (callback) {
    callback.call(img);
    }
    }

    if (img.src) {
    if (/^data:/i.test(img.src)) { // Data URI
    var arrayBuffer = base64ToArrayBuffer(img.src);
    handleBinaryFile(arrayBuffer);

    } else if (/^blob:/i.test(img.src)) { // Object URL
    var fileReader = new FileReader();
    fileReader.onload = function(e) {
    handleBinaryFile(e.target.result);
    };
    objectURLToBlob(img.src, function (blob) {
    fileReader.readAsArrayBuffer(blob);
    });
    } else {
    var http = new XMLHttpRequest();
    http.onload = function() {
    if (this.status == 200 || this.status === 0) {
    handleBinaryFile(http.response);
    } else {
    throw "Could not load image";
    }
    http = null;
    };
    http.open("GET", img.src, true);
    http.responseType = "arraybuffer";
    http.send(null);
    }
    } else if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) {
    var fileReader = new FileReader();
    fileReader.onload = function(e) {
    if (debug) console.log("Got file of length " + e.target.result.byteLength);
    handleBinaryFile(e.target.result);
    };

    fileReader.readAsArrayBuffer(img);
    }
    }

    function findEXIFinJPEG(file) {
    var dataView = new DataView(file);

    if (debug) console.log("Got file of length " + file.byteLength);
    if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
    if (debug) console.log("Not a valid JPEG");
    return false; // not a valid jpeg
    }

    var offset = 2,
    length = file.byteLength,
    marker;

    while (offset < length) {
    if (dataView.getUint8(offset) != 0xFF) {
    if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
    return false; // not a valid marker, something is wrong
    }

    marker = dataView.getUint8(offset + 1);
    if (debug) console.log(marker);

    // we could implement handling for other markers here,
    // but we're only looking for 0xFFE1 for EXIF data

    if (marker == 225) {
    if (debug) console.log("Found 0xFFE1 marker");

    return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);

    // offset += 2 + file.getShortAt(offset+2, true);

    } else {
    offset += 2 + dataView.getUint16(offset+2);
    }

    }

    }

    function findIPTCinJPEG(file) {
    var dataView = new DataView(file);

    if (debug) console.log("Got file of length " + file.byteLength);
    if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
    if (debug) console.log("Not a valid JPEG");
    return false; // not a valid jpeg
    }

    var offset = 2,
    length = file.byteLength;

    var isFieldSegmentStart = function(dataView, offset){
    return (
    dataView.getUint8(offset) === 0x38 &&
    dataView.getUint8(offset+1) === 0x42 &&
    dataView.getUint8(offset+2) === 0x49 &&
    dataView.getUint8(offset+3) === 0x4D &&
    dataView.getUint8(offset+4) === 0x04 &&
    dataView.getUint8(offset+5) === 0x04
    );
    };

    while (offset < length) {

    if ( isFieldSegmentStart(dataView, offset )){

    // Get the length of the name header (which is padded to an even number of bytes)
    var nameHeaderLength = dataView.getUint8(offset+7);
    if(nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
    // Check for pre photoshop 6 format
    if(nameHeaderLength === 0) {
    // Always 4
    nameHeaderLength = 4;
    }

    var startOffset = offset + 8 + nameHeaderLength;
    var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);

    return readIPTCData(file, startOffset, sectionLength);

    break;

    }

    // Not the marker, continue searching
    offset++;

    }

    }
    var IptcFieldMap = {
    0x78 : 'caption',
    0x6E : 'credit',
    0x19 : 'keywords',
    0x37 : 'dateCreated',
    0x50 : 'byline',
    0x55 : 'bylineTitle',
    0x7A : 'captionWriter',
    0x69 : 'headline',
    0x74 : 'copyright',
    0x0F : 'category'
    };
    function readIPTCData(file, startOffset, sectionLength){
    var dataView = new DataView(file);
    var data = {};
    var fieldValue, fieldName, dataSize, segmentType, segmentSize;
    var segmentStartPos = startOffset;
    while(segmentStartPos < startOffset+sectionLength) {
    if(dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02){
    segmentType = dataView.getUint8(segmentStartPos+2);
    if(segmentType in IptcFieldMap) {
    dataSize = dataView.getInt16(segmentStartPos+3);
    segmentSize = dataSize + 5;
    fieldName = IptcFieldMap[segmentType];
    fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize);
    // Check if we already stored a value with this name
    if(data.hasOwnProperty(fieldName)) {
    // Value already stored with this name, create multivalue field
    if(data[fieldName] instanceof Array) {
    data[fieldName].push(fieldValue);
    }
    else {
    data[fieldName] = [data[fieldName], fieldValue];
    }
    }
    else {
    data[fieldName] = fieldValue;
    }
    }

    }
    segmentStartPos++;
    }
    return data;
    }

    function readTags(file, tiffStart, dirStart, strings, bigEnd) {
    var entries = file.getUint16(dirStart, !bigEnd),
    tags = {},
    entryOffset, tag,
    i;

    for (i=0;i<entries;i++) {
    entryOffset = dirStart + i*12 + 2;
    tag = strings[file.getUint16(entryOffset, !bigEnd)];
    if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
    tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
    }
    return tags;
    }

    function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
    var type = file.getUint16(entryOffset+2, !bigEnd),
    numValues = file.getUint32(entryOffset+4, !bigEnd),
    valueOffset = file.getUint32(entryOffset+8, !bigEnd) + tiffStart,
    offset,
    vals, val, n,
    numerator, denominator;

    switch (type) {
    case 1: // byte, 8-bit unsigned int
    case 7: // undefined, 8-bit byte, value depending on field
    if (numValues == 1) {
    return file.getUint8(entryOffset + 8, !bigEnd);
    } else {
    offset = numValues > 4 ? valueOffset : (entryOffset + 8);
    vals = [];
    for (n=0;n<numValues;n++) {
    vals[n] = file.getUint8(offset + n);
    }
    return vals;
    }

    case 2: // ascii, 8-bit byte
    offset = numValues > 4 ? valueOffset : (entryOffset + 8);
    return getStringFromDB(file, offset, numValues-1);

    case 3: // short, 16 bit int
    if (numValues == 1) {
    return file.getUint16(entryOffset + 8, !bigEnd);
    } else {
    offset = numValues > 2 ? valueOffset : (entryOffset + 8);
    vals = [];
    for (n=0;n<numValues;n++) {
    vals[n] = file.getUint16(offset + 2*n, !bigEnd);
    }
    return vals;
    }

    case 4: // long, 32 bit int
    if (numValues == 1) {
    return file.getUint32(entryOffset + 8, !bigEnd);
    } else {
    vals = [];
    for (n=0;n<numValues;n++) {
    vals[n] = file.getUint32(valueOffset + 4*n, !bigEnd);
    }
    return vals;
    }

    case 5: // rational = two long values, first is numerator, second is denominator
    if (numValues == 1) {
    numerator = file.getUint32(valueOffset, !bigEnd);
    denominator = file.getUint32(valueOffset+4, !bigEnd);
    val = new Number(numerator / denominator);
    val.numerator = numerator;
    val.denominator = denominator;
    return val;
    } else {
    vals = [];
    for (n=0;n<numValues;n++) {
    numerator = file.getUint32(valueOffset + 8*n, !bigEnd);
    denominator = file.getUint32(valueOffset+4 + 8*n, !bigEnd);
    vals[n] = new Number(numerator / denominator);
    vals[n].numerator = numerator;
    vals[n].denominator = denominator;
    }
    return vals;
    }

    case 9: // slong, 32 bit signed int
    if (numValues == 1) {
    return file.getInt32(entryOffset + 8, !bigEnd);
    } else {
    vals = [];
    for (n=0;n<numValues;n++) {
    vals[n] = file.getInt32(valueOffset + 4*n, !bigEnd);
    }
    return vals;
    }

    case 10: // signed rational, two slongs, first is numerator, second is denominator
    if (numValues == 1) {
    return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset+4, !bigEnd);
    } else {
    vals = [];
    for (n=0;n<numValues;n++) {
    vals[n] = file.getInt32(valueOffset + 8*n, !bigEnd) / file.getInt32(valueOffset+4 + 8*n, !bigEnd);
    }
    return vals;
    }
    }
    }

    function getStringFromDB(buffer, start, length) {
    var outstr = "";
    for (var n = start; n < start+length; n++) {
    outstr += String.fromCharCode(buffer.getUint8(n));
    }
    return outstr;
    }

    function readEXIFData(file, start) {
    if (getStringFromDB(file, start, 4) != "Exif") {
    if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
    return false;
    }

    var bigEnd,
    tags, tag,
    exifData, gpsData,
    tiffOffset = start + 6;

    // test for TIFF validity and endianness
    if (file.getUint16(tiffOffset) == 0x4949) {
    bigEnd = false;
    } else if (file.getUint16(tiffOffset) == 0x4D4D) {
    bigEnd = true;
    } else {
    if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
    return false;
    }

    if (file.getUint16(tiffOffset+2, !bigEnd) != 0x002A) {
    if (debug) console.log("Not valid TIFF data! (no 0x002A)");
    return false;
    }

    var firstIFDOffset = file.getUint32(tiffOffset+4, !bigEnd);

    if (firstIFDOffset < 0x00000008) {
    if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset+4, !bigEnd));
    return false;
    }

    tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);

    if (tags.ExifIFDPointer) {
    exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
    for (tag in exifData) {
    switch (tag) {
    case "LightSource" :
    case "Flash" :
    case "MeteringMode" :
    case "ExposureProgram" :
    case "SensingMethod" :
    case "SceneCaptureType" :
    case "SceneType" :
    case "CustomRendered" :
    case "WhiteBalance" :
    case "GainControl" :
    case "Contrast" :
    case "Saturation" :
    case "Sharpness" :
    case "SubjectDistanceRange" :
    case "FileSource" :
    exifData[tag] = StringValues[tag][exifData[tag]];
    break;

    case "ExifVersion" :
    case "FlashpixVersion" :
    exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
    break;

    case "ComponentsConfiguration" :
    exifData[tag] =
    StringValues.Components[exifData[tag][0]] +
    StringValues.Components[exifData[tag][1]] +
    StringValues.Components[exifData[tag][2]] +
    StringValues.Components[exifData[tag][3]];
    break;
    }
    tags[tag] = exifData[tag];
    }
    }

    if (tags.GPSInfoIFDPointer) {
    gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
    for (tag in gpsData) {
    switch (tag) {
    case "GPSVersionID" :
    gpsData[tag] = gpsData[tag][0] +
    "." + gpsData[tag][1] +
    "." + gpsData[tag][2] +
    "." + gpsData[tag][3];
    break;
    }
    tags[tag] = gpsData[tag];
    }
    }

    return tags;
    }

    this.getData = function(img, callback) {
    if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false;

    if (!imageHasData(img)) {
    getImageData(img, callback);
    } else {
    if (callback) {
    callback.call(img);
    }
    }
    return true;
    }

    this.getTag = function(img, tag) {
    if (!imageHasData(img)) return;
    return img.exifdata[tag];
    }

    this.getAllTags = function(img) {
    if (!imageHasData(img)) return {};
    var a,
    data = img.exifdata,
    tags = {};
    for (a in data) {
    if (data.hasOwnProperty(a)) {
    tags[a] = data[a];
    }
    }
    return tags;
    }

    this.pretty = function(img) {
    if (!imageHasData(img)) return "";
    var a,
    data = img.exifdata,
    strPretty = "";
    for (a in data) {
    if (data.hasOwnProperty(a)) {
    if (typeof data[a] == "object") {
    if (data[a] instanceof Number) {
    strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "] ";
    } else {
    strPretty += a + " : [" + data[a].length + " values] ";
    }
    } else {
    strPretty += a + " : " + data[a] + " ";
    }
    }
    }
    return strPretty;
    }

    this.readFromBinaryFile = function(file) {
    return findEXIFinJPEG(file);
    }
    }]);

    crop.factory('cropHost', ['$document', 'cropAreaCircle', 'cropAreaSquare', 'cropEXIF', function($document, CropAreaCircle, CropAreaSquare, cropEXIF) {
    /* STATIC FUNCTIONS */

    // Get Element's Offset
    var getElementOffset=function(elem) {
    var box = elem.getBoundingClientRect();

    var body = document.body;
    var docElem = document.documentElement;

    var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
    var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

    var clientTop = docElem.clientTop || body.clientTop || 0;
    var clientLeft = docElem.clientLeft || body.clientLeft || 0;

    var top = box.top + scrollTop - clientTop;
    var left = box.left + scrollLeft - clientLeft;

    return { top: Math.round(top), left: Math.round(left) };
    };

    return function(elCanvas, opts, events){
    /* PRIVATE VARIABLES */

    // Object Pointers
    var ctx=null,
    image=null,
    theArea=null;

    // Dimensions
    var minCanvasDims=[500,500],
    maxCanvasDims=[500,500];

    // Result Image size
    var resImgSize=500;

    // Result Image type
    var resImgFormat='image/png';

    // Result Image quality
    var resImgQuality=null;

    /* PRIVATE FUNCTIONS */

    // 画那个裁剪框框
    function drawScene() {
    // clear canvas
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    if(image!==null) {
    // draw source image
    ctx.drawImage(image, 0, 0, ctx.canvas.width, ctx.canvas.height);

    ctx.save();

    // and make it darker
    ctx.fillStyle = 'rgba(0, 0, 0, 0.65)';
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

    ctx.restore();

    // draw Area
    theArea.draw();
    }
    }

    // Resets CropHost
    var resetCropHost=function() {
    if(image!==null) {
    theArea.setImage(image);
    var imageDims=[image.width, image.height],
    imageRatio=image.width/image.height,
    canvasDims=imageDims;

    if(canvasDims[0]>maxCanvasDims[0]) {
    canvasDims[0]=maxCanvasDims[0];
    canvasDims[1]=canvasDims[0]/imageRatio;
    } else if(canvasDims[0]<minCanvasDims[0]) {
    canvasDims[0]=minCanvasDims[0];
    canvasDims[1]=canvasDims[0]/imageRatio;
    }
    if(canvasDims[1]>maxCanvasDims[1]) {
    canvasDims[1]=maxCanvasDims[1];
    canvasDims[0]=canvasDims[1]*imageRatio;
    } else if(canvasDims[1]<minCanvasDims[1]) {
    canvasDims[1]=minCanvasDims[1];
    canvasDims[0]=canvasDims[1]*imageRatio;
    }
    elCanvas.prop('width',canvasDims[0]).prop('height',canvasDims[1]).css({'margin-left': -canvasDims[0]/2+'px', 'margin-top': -canvasDims[1]/2+'px'});

    theArea.setX(ctx.canvas.width/2);
    theArea.setY(ctx.canvas.height/2);
    theArea.setSize(Math.max(200, ctx.canvas.width*2, ctx.canvas.height*2));
    } else {
    elCanvas.prop('width',0).prop('height',0).css({'margin-top': 0});
    }

    drawScene();
    };

    /**
    * Returns event.changedTouches directly if event is a TouchEvent.
    * If event is a jQuery event, return changedTouches of event.originalEvent
    */
    var getChangedTouches=function(event){
    if(angular.isDefined(event.changedTouches)){
    return event.changedTouches;
    }else{
    return event.originalEvent.changedTouches;
    }
    };

    var onMouseMove = function (e) {
    if(image!==null) {
    var offset=getElementOffset(ctx.canvas),
    pageX, pageY;
    if(e.type === 'touchmove') {
    pageX=getChangedTouches(e)[0].pageX;
    pageY=getChangedTouches(e)[0].pageY;
    } else {
    pageX=e.pageX;
    pageY=e.pageY;
    }
    theArea.processMouseMove(pageX-offset.left, pageY-offset.top);
    drawScene();
    }
    };

    var onMouseDown=function(e) {
    e.preventDefault();
    e.stopPropagation();
    if(image!==null) {
    var offset=getElementOffset(ctx.canvas),
    pageX, pageY;
    if(e.type === 'touchstart') {
    pageX=getChangedTouches(e)[0].pageX;
    pageY=getChangedTouches(e)[0].pageY;
    } else {
    pageX=e.pageX;
    pageY=e.pageY;
    }
    theArea.processMouseDown(pageX-offset.left, pageY-offset.top);
    drawScene();
    }
    };

    var onMouseUp=function(e) {
    if(image!==null) {
    var offset=getElementOffset(ctx.canvas),
    pageX, pageY;
    if(e.type === 'touchend') {
    pageX=getChangedTouches(e)[0].pageX;
    pageY=getChangedTouches(e)[0].pageY;
    } else {
    pageX=e.pageX;
    pageY=e.pageY;
    }
    theArea.processMouseUp(pageX-offset.left, pageY-offset.top);
    drawScene();
    }
    };
    //裁剪实时跳转方法
    this.getResultImageDataURI = function () {
    var temp_ctx, temp_canvas;
    temp_canvas = angular.element('<canvas></canvas>')[0];
    temp_ctx = temp_canvas.getContext('2d');
    temp_canvas.width = resImgSize;
    // temp_canvas.height = 400;
    temp_canvas.height = resImgSize*7/10;
    if(image!==null){
    temp_ctx.drawImage(image, (theArea.getX()-theArea.getSize()/2)*(image.width/ctx.canvas.width), (theArea.getY()-theArea.getSize()*3/8)*(image.height/ctx.canvas.height), theArea.getSize()*(image.width/ctx.canvas.width), theArea.getSize()*(image.height/ctx.canvas.height)*3/4, 0, 0, resImgSize, resImgSize*3/4);
    }
    if (resImgQuality!==null ){
    return temp_canvas.toDataURL(resImgFormat, resImgQuality);
    }
    return temp_canvas.toDataURL(resImgFormat);
    };
    this.setNewImageSource=function(imageSource) {
    image=null;
    resetCropHost();
    events.trigger('image-updated');
    if (!!imageSource) {
    const bgImg = new Image();
    var newImage = new Image();
    if(imageSource.substring(0,4).toLowerCase()==='http') {
    newImage.crossOrigin = 'anonymous';
    }
    newImage.onload = function(){
    events.trigger('load-done');

    cropEXIF.getData(newImage,function(){
    var orientation=cropEXIF.getTag(newImage,'Orientation');
    if([3,6,8].indexOf(orientation)>-1) {
    var canvas = document.createElement("canvas"),
    ctx=canvas.getContext("2d"),
    cw = newImage.width, ch = newImage.height, cx = 0, cy = 0, deg=0;
    switch(orientation) {
    case 3:
    cx=-newImage.width;
    cy=-newImage.height;
    deg=180;
    break;
    case 6:
    cw = newImage.height;
    ch = newImage.width;
    cy=-newImage.height;
    deg=90;
    break;
    case 8:
    cw = newImage.height;
    ch = newImage.width;
    cx=-newImage.width;
    deg=270;
    break;
    }

    canvas.width = cw;
    canvas.height = ch;
    ctx.rotate(deg * Math.PI / 180);
    ctx.drawImage(bgImg, 0, 0, 260, 900);
    ctx.drawImage(newImage, cx, cy);

    image=new Image();
    image.src = canvas.toDataURL("image/png");
    } else {
    image=newImage;
    }
    resetCropHost();
    events.trigger('image-updated');
    });
    };
    newImage.onerror=function() {
    events.trigger('load-error');
    };
    events.trigger('load-start');
    newImage.src=imageSource;
    }
    };

    this.setMaxDimensions=function(width, height) {
    maxCanvasDims=[width,height];

    if(image!==null) {
    var curWidth=ctx.canvas.width,
    curHeight=ctx.canvas.height;

    var imageDims=[image.width, image.height],
    imageRatio=image.width/image.height,
    canvasDims=imageDims;

    if(canvasDims[0]>maxCanvasDims[0]) {
    canvasDims[0]=maxCanvasDims[0];
    canvasDims[1]=canvasDims[0]/imageRatio;
    } else if(canvasDims[0]<minCanvasDims[0]) {
    canvasDims[0]=minCanvasDims[0];
    canvasDims[1]=canvasDims[0]/imageRatio;
    }
    if(canvasDims[1]>maxCanvasDims[1]) {
    canvasDims[1]=maxCanvasDims[1];
    canvasDims[0]=canvasDims[1]*imageRatio;
    } else if(canvasDims[1]<minCanvasDims[1]) {
    canvasDims[1]=minCanvasDims[1];
    canvasDims[0]=canvasDims[1]*imageRatio;
    }
    elCanvas.prop('width',canvasDims[0]).prop('height',canvasDims[1]).css({'margin-left': -canvasDims[0]/2+'px', 'margin-top': -canvasDims[1]/2+'px'});

    var ratioNewCurWidth=ctx.canvas.width/curWidth,
    ratioNewCurHeight=ctx.canvas.height/curHeight,
    ratioMin=Math.min(ratioNewCurWidth, ratioNewCurHeight);

    theArea.setX(theArea.getX()*ratioNewCurWidth);
    theArea.setY(theArea.getY()*ratioNewCurHeight);
    theArea.setSize(theArea.getSize()*ratioMin);
    } else {
    elCanvas.prop('width',0).prop('height',0).css({'margin-top': 0});
    }

    drawScene();

    };
    //画小尺寸框框
    this.setAreaMinSize = function (size) {
    size = parseInt(size, 10);
    if(!isNaN(size)) {
    theArea.setMinSize(size);
    drawScene();
    }
    size = -1;
    };

    this.setResultImageSize=function(size) {
    size=parseInt(size,10);
    if(!isNaN(size)) {
    resImgSize=size;
    }
    };

    this.setResultImageFormat=function(format) {
    resImgFormat = format;
    };

    this.setResultImageQuality=function(quality){
    quality = parseFloat(quality);
    if (!isNaN(quality) && quality>=0 && quality<=1){
    resImgQuality = quality;
    }
    };

    this.setAreaType=function(type) {
    var curSize=theArea.getSize(),
    curMinSize=theArea.getMinSize(),
    curX=theArea.getX(),
    curY=theArea.getY();

    var AreaClass=CropAreaCircle;
    if(type==='square') {
    AreaClass=CropAreaSquare;
    }
    theArea = new AreaClass(ctx, events);
    theArea.setMinSize(curMinSize);
    theArea.setSize(curSize);
    theArea.setX(curX);
    theArea.setY(curY);

    // resetCropHost();
    if(image!==null) {
    theArea.setImage(image);
    }

    drawScene();
    };

    /* Life Cycle begins */

    // Init Context var
    ctx = elCanvas[0].getContext('2d');

    // Init CropArea
    theArea = new CropAreaCircle(ctx, events);

    // Init Mouse Event Listeners
    $document.on('mousemove',onMouseMove);
    elCanvas.on('mousedown',onMouseDown);
    $document.on('mouseup',onMouseUp);

    // Init Touch Event Listeners
    $document.on('touchmove',onMouseMove);
    elCanvas.on('touchstart',onMouseDown);
    $document.on('touchend',onMouseUp);

    // CropHost Destructor
    this.destroy=function() {
    $document.off('mousemove',onMouseMove);
    elCanvas.off('mousedown',onMouseDown);
    $document.off('mouseup',onMouseMove);

    $document.off('touchmove',onMouseMove);
    elCanvas.off('touchstart',onMouseDown);
    $document.off('touchend',onMouseMove);

    elCanvas.remove();
    };
    };

    }]);


    crop.factory('cropPubSub', [function() {
    return function() {
    var events = {};
    // Subscribe
    this.on = function(names, handler) {
    names.split(' ').forEach(function(name) {
    if (!events[name]) {
    events[name] = [];
    }
    events[name].push(handler);
    });
    return this;
    };
    // Publish
    this.trigger = function(name, args) {
    angular.forEach(events[name], function(handler) {
    handler.call(null, args);
    });
    return this;
    };
    };
    }]);

    crop.directive('imgCrop', ['$timeout', 'cropHost', 'cropPubSub', function($timeout, CropHost, CropPubSub) {
    return {
    restrict: 'E',
    scope: {
    image: '=',
    resultImage: '=',
    changeOnFly: '=',
    areaType: '@',
    areaMinSize: '=',
    resultImageSize: '=',
    resultImageFormat: '@',
    resultImageQuality: '=',
    onChange: '&',
    onLoadBegin: '&',
    onLoadDone: '&',
    onLoadError: '&'
    },
    template: '<canvas></canvas>',
    controller: ['$scope', function($scope) {
    $scope.events = new CropPubSub();
    }],
    link: function(scope, element/*, attrs*/) {
    // Init Events Manager
    var events = scope.events;

    // Init Crop Host
    var cropHost=new CropHost(element.find('canvas'), {}, events);

    // 存储结果图像以检查其是否已更改
    var storedResultImage;
    //更新裁剪好的图片的url
    var updateResultImage = function (scope) {
    var resultImage=cropHost.getResultImageDataURI();
    if(storedResultImage!==resultImage) {
    storedResultImage=resultImage;
    if(angular.isDefined(scope.resultImage)) {
    scope.resultImage=resultImage;
    }
    scope.onChange({$dataURI: scope.resultImage});
    }
    };
    // Wrapper to safely exec functions within $apply on a running $digest cycle
    var fnSafeApply=function(fn) {
    return function(){
    $timeout(function(){
    scope.$apply(function(scope){
    fn(scope);
    });
    });
    };
    };

    // Setup CropHost Event Handlers
    events
    .on('load-start', fnSafeApply(function(scope){
    scope.onLoadBegin({});
    }))
    .on('load-done', fnSafeApply(function(scope){
    scope.onLoadDone({});
    }))
    .on('load-error', fnSafeApply(function(scope){
    scope.onLoadError({});
    }))
    .on('area-move area-resize', fnSafeApply(function(scope){
    if(!!scope.changeOnFly) {
    updateResultImage(scope);
    }
    }))
    .on('area-move-end area-resize-end image-updated', fnSafeApply(function(scope){
    updateResultImage(scope);
    }));

    // Sync CropHost with Directive's options
    scope.$watch('image',function(){
    cropHost.setNewImageSource(scope.image);
    });
    scope.$watch('areaType',function(){
    cropHost.setAreaType(scope.areaType);
    updateResultImage(scope);
    });
    scope.$watch('areaMinSize',function(){
    cropHost.setAreaMinSize(scope.areaMinSize);
    updateResultImage(scope);
    });
    scope.$watch('resultImageSize',function(){
    cropHost.setResultImageSize(scope.resultImageSize);
    updateResultImage(scope);
    });
    scope.$watch('resultImageFormat',function(){
    cropHost.setResultImageFormat(scope.resultImageFormat);
    updateResultImage(scope);
    });
    scope.$watch('resultImageQuality',function(){
    cropHost.setResultImageQuality(scope.resultImageQuality);
    updateResultImage(scope);
    });

    // Update CropHost dimensions when the directive element is resized
    scope.$watch(
    function () {
    return [element[0].clientWidth, element[0].clientHeight];
    },
    function (value) {
    cropHost.setMaxDimensions(value[0],value[1]);
    updateResultImage(scope);
    },
    true
    );

    // Destroy CropHost Instance when the directive is destroying
    scope.$on('$destroy', function(){
    cropHost.destroy();
    });
    }
    };
    }]);
    }());

  • 相关阅读:
    Leetcode979 : Distribute Coins in Binary Tree 二叉树均匀分配硬币问题
    Leetcode547: Friend Circles 朋友圈问题
    线程安全的生产者消费者四种实现方法
    Leetcode823 : 因子二叉树问题
    Leetcode7 : Reverse Integer 整数反转问题
    Leetcode3 : 最长不含重复字符子串问题
    Leetcode6 : ZigZag 转换问题
    leetcode8 : 实现atoi函数将字符串转化为整数问题
    seo域名选择
    利用 Python django 框架 输入汉字,数字,字符,等。。转成二维码!
  • 原文地址:https://www.cnblogs.com/sky-liu/p/13896447.html
Copyright © 2011-2022 走看看