123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977 |
- /*!
- * 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.resSizeScale=1;
- 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.resSizeScale],
- [this._x + hSize, this._y - hSize/this.resSizeScale],
- [this._x - hSize, this._y + hSize/this.resSizeScale],
- [this._x + hSize, this._y + hSize/this.resSizeScale]
- ];
- };
- 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,scale) {
- var hSize = size / 2;
- scale=scale||1;
- ctx.rect(centerCoords[0] - hSize, centerCoords[1] - hSize/scale, size, size/scale);
- };
- 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.resSizeScale = 1; //图片剪切比例
- 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*this.resSizeScale,
- 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.resSizeScale) { this._y = this._size / 2/this.resSizeScale; }
- if (this._y > (h - this._size / 2)/this.resSizeScale) { this._y = (h - this._size / 2)/this.resSizeScale; }
- };
- CropArea.prototype._drawArea = function() {};
- CropArea.prototype.draw = function() {
- // draw crop area
- this._cropCanvas.drawCropArea(this._image, [this._x, this._y], this._size,this.resSizeScale, 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,scale, 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,scale);
- 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,scale);
- 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;
- //图片剪切比例
- var resSizeScale = 1;
- // 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]);
- 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 / resSizeScale;
- if (image !== null) {
- temp_ctx.drawImage(image, (theArea.getX() - theArea.getSize() / 2) * (image.width / ctx.canvas.width), (theArea.getY() - theArea.getSize() / 2 / resSizeScale) * (image.height / ctx.canvas.height), theArea.getSize() * (image.width / ctx.canvas.width), theArea.getSize() * (image.height / ctx.canvas.height) / resSizeScale, 0, 0, resImgSize, resImgSize / resSizeScale);
- }
- 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]);
- 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, scale) {
- size = parseInt(size, 10);
- resSizeScale = scale || 1;
- 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, scale) {
- 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.resSizeScale = scale||1;
- 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: '=',
- resultBlob: '=',
- changeOnFly: '=',
- areaType: '@',
- areaMinSize: '=',
- resultImageSize: '=',
- sizeScale: '=',
- 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;
- function dataURItoBlob(dataURI) {
- var byteString,
- mimestring;
- if (dataURI.split(',')[0].indexOf('base64') !== -1) {
- byteString = atob(dataURI.split(',')[1]);
- } else {
- byteString = decodeURI(dataURI.split(',')[1]);
- }
- mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0];
- var content = new Array();
- for (var i = 0; i < byteString.length; i++) {
- content[i] = byteString.charCodeAt(i);
- }
- return new Blob([new Uint8Array(content)], { type: mimestring });
- }
- var updateResultImage = function(scope) {
- var resultImage = cropHost.getResultImageDataURI();
- if (storedResultImage !== resultImage) {
- storedResultImage = resultImage;
- if (angular.isDefined(scope.resultImage)) {
- scope.resultBlob = dataURItoBlob(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, scope.sizeScale);
- updateResultImage(scope);
- });
- scope.$watch('areaMinSize', function() {
- cropHost.setAreaMinSize(scope.areaMinSize);
- updateResultImage(scope);
- });
- scope.$watch('resultImageSize', function() {
- cropHost.setResultImageSize(scope.resultImageSize, scope.sizeScale);
- 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
- $timeout(function() {
- cropHost.setMaxDimensions(element[0].clientWidth, element[0].clientHeight - 4);
- updateResultImage(scope);
- }, true)
- // 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();
- });
- }
- };
- }]);
- }());
|