1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879 |
- /*!
- * ngImgCrop v0.3.2
- * https://github.com/alexk111/ngImgCrop
- *
- * Copyright (c) 2014 Alex Kaul
- * License: MIT
- *
- * Generated at Wednesday, December 3rd, 2014, 3:54: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 = 20;
- 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=0;
- 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], 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._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=0;
- 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;
- return [
- [this._x-hSize, this._y-hSize],
- [this._x+hSize, this._y-hSize],
- [this._x-hSize, this._y+hSize],
- [this._x+hSize, this._y+hSize]
- ];
- };
- CropAreaSquare.prototype._calcSquareDimensions=function() {
- var hSize=this._size/2;
- return {
- left: this._x-hSize,
- top: this._y-hSize,
- right: this._x+hSize,
- bottom: this._y+hSize
- };
- };
- 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;
- ctx.rect(centerCoords[0]-hSize,centerCoords[1]-hSize,size,size);
- };
- 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=80;
- this._cropCanvas=new CropCanvas(ctx);
- this._image=new Image();
- this._x = 0;
- this._y = 0;
- this._size = 200;
- };
- /* 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 = 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(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/2) { this._y=this._size/2; }
- if(this._y>h-this._size/2) { this._y=h-this._size/2; }
- };
- 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]];
- };
- // Draw Filled Polygon
- 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);
- ctx.strokeRect(centerCoords[0] - scaledBoxSize/2, centerCoords[1] - scaledBoxSize/2, scaledBoxSize, scaledBoxSize);
- 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();
- };
- };
- }]);
- /**
- * EXIF service is based on the exif-js library (https://github.com/jseidelin/exif-js)
- */
- 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 + "]\r\n";
- } else {
- strPretty += a + " : [" + data[a].length + " values]\r\n";
- }
- } else {
- strPretty += a + " : " + data[a] + "\r\n";
- }
- }
- }
- 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=[100,100],
- maxCanvasDims=[300,300];
- // Result Image size
- var resImgSize=200;
- // Result Image type
- var resImgFormat='image/png';
- // Result Image quality
- var resImgQuality=null;
- /* PRIVATE FUNCTIONS */
- // Draw Scene
- 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.min(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 = resImgSize;
- if(image!==null){
- temp_ctx.drawImage(image, (theArea.getX()-theArea.getSize()/2)*(image.width/ctx.canvas.width), (theArea.getY()-theArea.getSize()/2)*(image.height/ctx.canvas.height), theArea.getSize()*(image.width/ctx.canvas.width), theArea.getSize()*(image.height/ctx.canvas.height), 0, 0, resImgSize, resImgSize);
- }
- 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) {
- 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(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();
- }
- };
- 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);
- // Store Result Image to check if it's changed
- var storedResultImage;
- 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();
- });
- }
- };
- }]);
- }());
|