bgraimagemanipulation.pas 122 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092
  1. // SPDX-License-Identifier: LGPL-3.0-linking-exception
  2. unit BGRAImageManipulation;
  3. { ============================================================================
  4. BGRAImageManipulation Unit
  5. originally written in 2011 by - Emerson Cavalcanti <emersoncavalcanti at googlesites>
  6. ============================================================================
  7. Description:
  8. TBGRAImageManipulation is a component designed to make simple changes in an
  9. image while maintaining the aspect ratio of the final image and allow it to
  10. cut to reduce the unnecessary edges. The selected area is painted with a
  11. different transparency level for easy viewing of what will be cut.
  12. ============================================================================
  13. History:
  14. 2011-05-03 - Emerson Cavalcanti
  15. - Initial version
  16. 2011-06-01 - Emerson Cavalcanti
  17. - Fixed aspect ratio when the image has a dimension smaller than
  18. the size of the component.
  19. - Fixed memory leak on temporary bitmaps.
  20. - Fixed unecessary release of bitmap.
  21. - Inserted Anchor and Align property on component.
  22. - Implemented 'Keep aspect Ratio' property. Now you can select an
  23. area without maintaining the aspect ratio.
  24. 2011-06-03 - Emerson Cavalcanti
  25. - Improved selection when don't use aspect ratio.
  26. - Improved response when resize component.
  27. - Fixed memory leak on resample bitmap.
  28. 2011-06-04 - Circular
  29. - Fixed divide by zero when calculate aspect ratio on
  30. getImageRect.
  31. 2011-06-07 - Emerson Cavalcanti
  32. - Improved function of aspect ratio including a variable to
  33. provide the value directly in the component, instead of using
  34. the dimensions of the component as the source of this value.
  35. - Improved exhibition of anchors on selection.
  36. - Improved mouse cursor.
  37. - Included function to get the aspect ratio from image size.
  38. - Included rotate Left and Right functions.
  39. 2013-10-13 - Massimo Magnano
  40. - Add multi crop areas
  41. - Add get Bitmap not resampled (original scale)
  42. 2014-08-04 - lainz-007-
  43. - Included DataType.inc inside the unit
  44. 2021-03-30 - Massimo Magnano
  45. - Each CropArea has its own AspectRatio, Add Events, Border Color
  46. 2021-04-30 - Massimo Magnano
  47. - CropArea list Load/Save, bug fixes
  48. 2023-06 - Massimo Magnano
  49. - the CropArea.Area property is relative to the unscaled image (unused in render/mouse events)
  50. - added CropArea.ScaledArea property relative to the scaled image (used in render/mouse events)
  51. - removed the use of DeltaX, DeltaY in render/mouse/etc
  52. - CropAreas Area and ScaledArea property is updated during the mouse events
  53. - rewriting of the methods for taking cropped images
  54. -08 - the CropArea.Area property can be specified in Pixels,Cm,Inch
  55. - Alt on MouseUp Undo the Crop Area Changes,Optimized mouse events
  56. -09 - OverAnchor gives precedence to the selected area than Z Order
  57. - EmptyImage property; CropAreas when Image is Empty; Old Code deleted and optimized
  58. - XML Use Laz2_XMLCfg in fpc
  59. - divide by zero in getImageRect on Component Loading
  60. - EmptyImage size to ClientRect when Width/Height=0; Mouse Events when Image is Empty
  61. - CropArea Rotate and Flip
  62. - CropArea Duplicate and SetSize
  63. - NewCropAreaDefault property (to Cm); ResolutionUnitConvert function; SetEmptyImageSizeToCropAreas
  64. -10 - Load/Save XML Path Parameters, ContextMenu, UserData in GetAllBitmapCallback, CropArea Icons
  65. 2024-01 - Added CopyProperties to GetBitmap methods
  66. -06 - Solved Bugs when load/save from xml
  67. -08 - Removed EmptyImage.Allow, so is always allowed
  68. CopyPropertiesToArea and Icons in NewCropAreaDefault
  69. Updated Component icon
  70. ============================================================================
  71. }
  72. {******************************* CONTRIBUTOR(S) ******************************
  73. - Edivando S. Santos Brasil | mailedivando@gmail.com
  74. (Compatibility with delphi VCL 11/2018)
  75. ***************************** END CONTRIBUTOR(S) *****************************}
  76. {$I bgracontrols.inc}
  77. interface
  78. {$IFDEF FPC}
  79. {$DEFINE USE_Laz2_XMLCfg}
  80. {$ENDIF}
  81. uses
  82. Classes, Contnrs, SysUtils,
  83. {$IFDEF FPC}LCLIntf, LResources, FPImage, {$ENDIF}
  84. Forms, Controls, Graphics, Dialogs,
  85. {$IFNDEF FPC}Windows, Messages, BGRAGraphics, GraphType,{$ENDIF}
  86. {$IFDEF USE_Laz2_XMLCfg}Laz2_XMLCfg,{$ELSE}XMLConf,{$ENDIF}
  87. BCBaseCtrls, BGRABitmap, BGRABitmapTypes, BGRAGradientScanner;
  88. {$IFNDEF FPC}
  89. const
  90. crSizeNW = TCursor(-23);
  91. crSizeN = TCursor(-24);
  92. crSizeNE = TCursor(-25);
  93. crSizeW = TCursor(-26);
  94. crSizeE = TCursor(-27);
  95. crSizeSW = TCursor(-28);
  96. crSizeS = TCursor(-29);
  97. crSizeSE = TCursor(-30);
  98. crUpArrow = TCursor(-10);
  99. crHourGlass = TCursor(-11);
  100. crDrag = TCursor(-12);
  101. crNoDrop = TCursor(-13);
  102. crHSplit = TCursor(-14);
  103. crVSplit = TCursor(-15);
  104. crMultiDrag = TCursor(-16);
  105. {$ENDIF}
  106. type
  107. TCoord = packed record
  108. x1 : LongInt;
  109. y1 : LongInt;
  110. x2 : LongInt;
  111. y2 : LongInt;
  112. end;
  113. TRatio = packed record
  114. Horizontal : LongInt;
  115. Vertical : LongInt;
  116. end;
  117. TCardinalDirection = (NORTH, SOUTH, WEST, EAST);
  118. TDirection = set of TCardinalDirection;
  119. TSizeLimits = packed record
  120. minWidth : LongInt;
  121. maxWidth : LongInt;
  122. minHeight : LongInt;
  123. maxHeight : LongInt;
  124. end;
  125. TBGRAImageManipulation = class;
  126. TCropAreaList = class;
  127. { TCropArea }
  128. BoolParent = (bFalse=0, bTrue=1, bParent=2);
  129. TCropAreaIcons = set of (cIcoIndex, cIcoLockSize, cIcoLockMove);
  130. TCropArea = class(TObject)
  131. protected
  132. fOwner :TBGRAImageManipulation;
  133. OwnerList:TCropAreaList;
  134. rScaledArea:TRect;
  135. rArea :TRectF;
  136. rAreaUnit:TResolutionUnit;
  137. rRatio :TRatio;
  138. rAspectX,
  139. rAspectY,
  140. rMinHeight,
  141. rMinWidth : Integer;
  142. rAspectRatio,
  143. rName: String;
  144. rKeepAspectRatio: BoolParent;
  145. Loading :Boolean;
  146. rIcons: TCropAreaIcons;
  147. procedure CopyAspectFromParent;
  148. procedure setAspectRatio(AValue: string);
  149. procedure setKeepAspectRatio(AValue: BoolParent);
  150. procedure setScaledArea(AValue: TRect);
  151. function getLeft: Single;
  152. procedure setLeft(AValue: Single);
  153. function getTop: Single;
  154. procedure setTop(AValue: Single);
  155. function getWidth: Single;
  156. procedure setWidth(AValue: Single);
  157. function getHeight: Single;
  158. procedure setHeight(AValue: Single);
  159. function getMaxHeight: Single;
  160. function getMaxWidth: Single;
  161. function getRealAspectRatio(var ARatio: TRatio):Boolean; //return Real KeepAspect
  162. function getRealKeepAspectRatio:Boolean;
  163. function getIndex: Longint;
  164. function getIsNullSize: Boolean;
  165. procedure setArea(AValue: TRectF);
  166. procedure setAreaUnit(AValue: TResolutionUnit);
  167. procedure setName(AValue: String);
  168. procedure setIcons(AValue: TCropAreaIcons);
  169. procedure Render_Refresh;
  170. procedure GetImageResolution(var resX, resY:Single; var resUnit:TResolutionUnit);
  171. procedure CalculateScaledAreaFromArea;
  172. procedure CalculateAreaFromScaledArea;
  173. function GetPixelArea(const AValue: TRectF):TRect;
  174. function CheckScaledOutOfBounds(var AArea:TRect):Boolean;
  175. function CheckAreaOutOfBounds(var AArea:TRectF):Boolean;
  176. property ScaledArea :TRect read rScaledArea write setScaledArea;
  177. public
  178. Rotate :Single;
  179. UserData :Integer;
  180. BorderColor :TBGRAPixel;
  181. function getResampledBitmap(ACopyProperties: Boolean=False): TBGRABitmap;
  182. function getBitmap(ACopyProperties: Boolean=False): TBGRABitmap;
  183. constructor Create(AOwner: TBGRAImageManipulation; AArea: TRectF;
  184. AAreaUnit: TResolutionUnit = ruNone; //Pixels
  185. AUserData: Integer = -1); overload;
  186. constructor Create(AOwner: TBGRAImageManipulation;
  187. DuplicateFrom: TCropArea; InsertInList:Boolean); overload;
  188. destructor Destroy; override;
  189. //ZOrder
  190. procedure BringToFront;
  191. procedure BringToBack;
  192. procedure BringForward;
  193. procedure BringBackward;
  194. //Rotate/Flip
  195. procedure RotateLeft;
  196. procedure RotateRight;
  197. procedure FlipHLeft;
  198. procedure FlipHRight;
  199. procedure FlipVUp;
  200. procedure FlipVDown;
  201. procedure SetSize(AWidth, AHeight:Single);
  202. property Area:TRectF read rArea write setArea;
  203. property AreaUnit:TResolutionUnit read rAreaUnit write setAreaUnit;
  204. property Top:Single read getTop write setTop;
  205. property Left:Single read getLeft write setLeft;
  206. property Width:Single read getWidth write setWidth;
  207. property Height:Single read getHeight write setHeight;
  208. property MaxWidth:Single read getMaxWidth;
  209. property MaxHeight:Single read getMaxHeight;
  210. property AspectRatio: string read rAspectRatio write setAspectRatio;
  211. property KeepAspectRatio: BoolParent read rKeepAspectRatio write setKeepAspectRatio default bParent;
  212. property Index:Longint read getIndex;
  213. property Name:String read rName write setName;
  214. property isNullSize: Boolean read getIsNullSize;
  215. property Icons:TCropAreaIcons read rIcons write setIcons;
  216. end;
  217. { TCropAreaList }
  218. TCropAreaList = class(TObjectList)
  219. protected
  220. fOwner :TBGRAImageManipulation;
  221. rName :String;
  222. rLoading :Boolean;
  223. function getCropArea(aIndex: Integer): TCropArea;
  224. procedure setCropArea(aIndex: Integer; const Value: TCropArea);
  225. procedure setLoading(AValue: Boolean);
  226. procedure Notify(Ptr: Pointer; Action: TListNotification); override;
  227. property Loading :Boolean read rLoading write setLoading;
  228. public
  229. constructor Create(AOwner: TBGRAImageManipulation);
  230. function add(aCropArea: TCropArea): integer;
  231. procedure Load(const XMLConf: TXMLConfig; XMLPath: String='');
  232. procedure Save(const XMLConf: TXMLConfig; XMLPath: String='');
  233. procedure LoadFromStream(Stream: TStream; XMLPath: String='');
  234. procedure LoadFromFile(const FileName: String; XMLPath: String='');
  235. procedure SaveToStream(Stream: TStream; XMLPath: String='');
  236. procedure SaveToFile(const FileName: String; XMLPath: String='');
  237. //Rotate/Flip
  238. procedure RotateLeft;
  239. procedure RotateRight;
  240. procedure FlipHLeft;
  241. procedure FlipHRight;
  242. procedure FlipVUp;
  243. procedure FlipVDown;
  244. property items[aIndex: integer] : TCropArea read getCropArea write setCropArea; default;
  245. property Name:String read rName write rName;
  246. end;
  247. TgetAllBitmapsCallback = procedure (Bitmap :TBGRABitmap; CropArea: TCropArea; AUserData:Integer) of object;
  248. { TBGRAEmptyImage }
  249. TBGRAEmptyImage = class(TPersistent)
  250. private
  251. fOwner: TBGRAImageManipulation;
  252. rResolutionHeight: Single;
  253. rResolutionUnit: TResolutionUnit;
  254. rResolutionWidth: Single;
  255. rShowBorder: Boolean;
  256. function getHeight: Integer;
  257. function getWidth: Integer;
  258. procedure SetResolutionUnit(AValue: TResolutionUnit);
  259. public
  260. property Width:Integer read getWidth;
  261. property Height:Integer read getHeight;
  262. constructor Create(AOwner: TBGRAImageManipulation);
  263. published
  264. property ResolutionUnit: TResolutionUnit read rResolutionUnit write SetResolutionUnit default ruPixelsPerCentimeter;
  265. property ResolutionWidth: Single read rResolutionWidth write rResolutionWidth;
  266. property ResolutionHeight: Single read rResolutionHeight write rResolutionHeight;
  267. property ShowBorder: Boolean read rShowBorder write rShowBorder default False;
  268. end;
  269. { TBGRANewCropAreaDefault }
  270. TBGRANewCropAreaDefault = class(TPersistent)
  271. private
  272. fOwner: TBGRAImageManipulation;
  273. rAspectRatio: string;
  274. rIcons: TCropAreaIcons;
  275. rKeepAspectRatio: BoolParent;
  276. rResolutionUnit: TResolutionUnit;
  277. public
  278. constructor Create(AOwner: TBGRAImageManipulation);
  279. procedure CopyPropertiesToArea(ANewArea: TCropArea);
  280. published
  281. property Icons: TCropAreaIcons read rIcons write rIcons;
  282. property ResolutionUnit: TResolutionUnit read rResolutionUnit write rResolutionUnit default ruPixelsPerCentimeter;
  283. property AspectRatio: string read rAspectRatio write rAspectRatio;
  284. property KeepAspectRatio: BoolParent read rKeepAspectRatio write rKeepAspectRatio default bFalse;
  285. end;
  286. { TBGRAImageManipulation }
  287. TCropAreaEvent = procedure (Sender: TBGRAImageManipulation; CropArea: TCropArea) of object;
  288. TCropAreaLoadEvent = function (Sender: TBGRAImageManipulation; CropArea: TCropArea;
  289. const XMLConf: TXMLConfig; const Path:String):Integer of object;
  290. TCropAreaSaveEvent = procedure (Sender: TBGRAImageManipulation; CropArea: TCropArea;
  291. const XMLConf: TXMLConfig; const Path:String) of object;
  292. TBGRAIMContextPopupEvent = procedure(Sender: TBGRAImageManipulation; CropArea: TCropArea;
  293. AnchorSelected :TDirection; MousePos: TPoint; var Handled: Boolean) of object;
  294. TBGRAImageManipulation = class(TBGRAGraphicCtrl)
  295. private
  296. { Private declarations }
  297. fAnchorSize: byte;
  298. fAnchorSelected: TDirection;
  299. fBorderSize: byte;
  300. fAspectRatio: string;
  301. fAspectX: integer;
  302. fAspectY: integer;
  303. fKeepAspectRatio: boolean;
  304. fMinHeight: integer;
  305. fMinWidth: integer;
  306. fMouseCaught: boolean;
  307. fStartPoint,
  308. fEndPoint: TPoint;
  309. fStartArea: TRect;
  310. fRatio: TRatio;
  311. fSizeLimits: TSizeLimits;
  312. fImageBitmap, fResampledBitmap, fBackground, fVirtualScreen: TBGRABitmap;
  313. rNewCropAreaDefault: TBGRANewCropAreaDefault;
  314. rOnContextPopup: TBGRAIMContextPopupEvent;
  315. function getAnchorSize: byte;
  316. function getPixelsPerInch: Integer;
  317. procedure setAnchorSize(const Value: byte);
  318. function getEmpty: boolean;
  319. procedure setBitmap(const Value: TBGRABitmap);
  320. procedure setBorderSize(const Value: byte);
  321. procedure setAspectRatio(const Value: string);
  322. procedure setEmptyImage(AValue: TBGRAEmptyImage);
  323. procedure setKeepAspectRatio(const Value: boolean);
  324. procedure setMinHeight(const Value: integer);
  325. procedure setMinWidth(const Value: integer);
  326. procedure setSelectedCropArea(AValue: TCropArea);
  327. protected
  328. { Protected declarations }
  329. rCropAreas :TCropAreaList;
  330. rNewCropArea,
  331. rSelectedCropArea :TCropArea;
  332. rOnCropAreaAdded: TCropAreaEvent;
  333. rOnCropAreaDeleted: TCropAreaEvent;
  334. rOnCropAreaChanged: TCropAreaEvent;
  335. rOnSelectedCropAreaChanged: TCropAreaEvent;
  336. rOnCropAreaLoad: TCropAreaLoadEvent;
  337. rOnCropAreaSave: TCropAreaSaveEvent;
  338. rEmptyImage: TBGRAEmptyImage;
  339. rLoading: Boolean;
  340. function ApplyDimRestriction(Coords: TCoord; Direction: TDirection; Bounds: TRect; AKeepAspectRatio:Boolean): TCoord;
  341. function ApplyRatioToAxes(Coords: TCoord; Direction: TDirection; Bounds: TRect; ACropArea :TCropArea = Nil): TCoord;
  342. procedure ApplyRatioToArea(ACropArea :TCropArea);
  343. procedure CalcMaxSelection(ACropArea :TCropArea);
  344. procedure findSizeLimits;
  345. function getDirection(const Point1, Point2: TPoint): TDirection;
  346. function getImageRect(Picture: TBGRABitmap): TRect;
  347. function getWorkRect: TRect;
  348. function isOverAnchor(APoint :TPoint; var AnchorSelected :TDirection; var ACursor :TCursor) :TCropArea;
  349. procedure CreateEmptyImage;
  350. procedure CreateResampledBitmap;
  351. procedure Loaded; override;
  352. procedure Paint; override;
  353. procedure RepaintBackground;
  354. procedure Resize; override;
  355. procedure Render;
  356. procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override;
  357. procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
  358. procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override;
  359. procedure DoContextPopup(MousePos: TPoint; var Handled: Boolean); override;
  360. public
  361. { Public declarations }
  362. constructor Create(AOwner: TComponent); override;
  363. destructor Destroy; override;
  364. procedure Invalidate; override;
  365. function getAspectRatioFromImage(const Value: TBGRABitmap): string;
  366. function getResampledBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False) : TBGRABitmap;
  367. function getBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False) : TBGRABitmap;
  368. procedure rotateLeft(ACopyProperties: Boolean=False);
  369. procedure rotateRight(ACopyProperties: Boolean=False);
  370. procedure tests;
  371. //Crop Areas Manipulation functions
  372. function addCropArea(AArea : TRectF; AAreaUnit: TResolutionUnit = ruNone;
  373. AUserData: Integer = -1) :TCropArea;
  374. function addScaledCropArea(AArea : TRect; AUserData: Integer = -1) :TCropArea;
  375. procedure delCropArea(ACropArea :TCropArea);
  376. procedure clearCropAreas;
  377. procedure getAllResampledBitmaps(ACallBack :TgetAllBitmapsCallback; AUserData:Integer=0; ACopyProperties: Boolean=False);
  378. procedure getAllBitmaps(ACallBack :TgetAllBitmapsCallback; AUserData:Integer=0; ACopyProperties: Boolean=False);
  379. procedure SetEmptyImageSizeToCropAreas(ReduceLarger: Boolean=False);
  380. procedure SetEmptyImageSizeToNull;
  381. procedure SetEmptyImageSize(AResolutionUnit: TResolutionUnit; AResolutionWidth, AResolutionHeight: Single);
  382. property SelectedCropArea :TCropArea read rSelectedCropArea write setSelectedCropArea;
  383. property CropAreas :TCropAreaList read rCropAreas;
  384. property PixelsPerInch: Integer read getPixelsPerInch;
  385. published
  386. { Published declarations }
  387. property Align;
  388. property Anchors;
  389. property AnchorSize: byte Read getAnchorSize Write setAnchorSize default 5;
  390. property Bitmap: TBGRABitmap Read fImageBitmap Write setBitmap;
  391. property BorderSize: byte Read fBorderSize Write setBorderSize default 2;
  392. property AspectRatio: string Read fAspectRatio Write setAspectRatio;
  393. property KeepAspectRatio: boolean Read fKeepAspectRatio Write setKeepAspectRatio default True;
  394. property MinHeight: integer Read fMinHeight Write setMinHeight;
  395. property MinWidth: integer Read fMinWidth Write setMinWidth;
  396. property Empty: boolean Read getEmpty;
  397. property EmptyImage: TBGRAEmptyImage read rEmptyImage write setEmptyImage stored True;
  398. property NewCropAreaDefault: TBGRANewCropAreaDefault read rNewCropAreaDefault write rNewCropAreaDefault stored True;
  399. //Events
  400. property OnCropAreaAdded:TCropAreaEvent read rOnCropAreaAdded write rOnCropAreaAdded;
  401. property OnCropAreaDeleted:TCropAreaEvent read rOnCropAreaDeleted write rOnCropAreaDeleted;
  402. property OnCropAreaChanged:TCropAreaEvent read rOnCropAreaChanged write rOnCropAreaChanged;
  403. property OnCropAreaLoad:TCropAreaLoadEvent read rOnCropAreaLoad write rOnCropAreaLoad;
  404. property OnCropAreaSave:TCropAreaSaveEvent read rOnCropAreaSave write rOnCropAreaSave;
  405. //CropArea Parameter is the Old Selected Area, use SelectedCropArea property for current
  406. property OnSelectedCropAreaChanged:TCropAreaEvent read rOnSelectedCropAreaChanged write rOnSelectedCropAreaChanged;
  407. property OnContextPopup: TBGRAIMContextPopupEvent read rOnContextPopup write rOnContextPopup;
  408. (* property OnStartDrag: TStartDragEvent;
  409. property OnDragDrop: TDragDropEvent;
  410. property OnDragOver: TDragOverEvent;
  411. property OnEndDrag: TEndDragEvent;*)
  412. end;
  413. function RoundUp(AValue:Single):Integer;
  414. function ResolutionUnitConvert(const AValue:Single; fromRes, toRes:TResolutionUnit; predefInchRes:Integer=96):Single;
  415. procedure PixelXResolutionUnitConvert(var resX, resY:Single; fromRes, toRes:TResolutionUnit);
  416. {$IFDEF FPC}procedure Register;{$ENDIF}
  417. implementation
  418. uses Math, ExtCtrls;
  419. resourcestring
  420. SAnchorSizeIsTooLarge =
  421. 'Anchor size is too large. %d is not within the valid range of %d..%d';
  422. SAnchorSizeIsTooSmall =
  423. 'Anchor size is too small. %d is not within the valid range of %d..%d';
  424. SAnchorSizeIsNotOdd = 'Anchor size is invalid. %d is not an odd number.';
  425. SBorderSizeIsTooLarge =
  426. 'Border size is too large. %d is not within the valid range of %d..%d';
  427. SBorderSizeIsTooSmall =
  428. 'Border size is too small. %d is not within the valid range of %d..%d';
  429. SAspectRatioIsNotValid = 'Aspect ratio value is invalid. %s contain invalid number.';
  430. { Calculate the Greatest Common Divisor (GCD) using the algorithm of Euclides }
  431. function getGCD(Nr1, Nr2: longint): longint;
  432. begin
  433. if Nr2 = 0 then
  434. Result := Nr1
  435. else
  436. Result := getGCD(Nr2, Nr1 mod Nr2);
  437. end;
  438. { Calculate the Lowest Common Multiple (LCM) using the algorithm of Euclides }
  439. function getLCM(Nr1, Nr2: longint): longint;
  440. begin
  441. Result := (Nr1 * Nr2) div getGCD(Nr1, Nr2);
  442. end;
  443. procedure CheckAspectRatio(const Value :String; var AspectRatioText :String; var XValue, YValue :Integer);
  444. const
  445. ValidChars = ['0'..'9', ':'];
  446. var
  447. Count :Integer;
  448. begin
  449. if ((pos(':', Value) > 0) and (pos(':', Value) < Length(Value))) then
  450. begin
  451. // Check if value is valid
  452. XValue := 0;
  453. YValue := 0;
  454. AspectRatioText := '';
  455. for Count := 1 to Length(Value) do
  456. begin
  457. if (Value[Count] in ValidChars) then
  458. begin
  459. if ((Value[Count] = ':') and (Length(AspectRatioText) > 0) and
  460. (XValue = 0)) then
  461. begin
  462. XValue := StrToInt(AspectRatioText);
  463. end;
  464. AspectRatioText := AspectRatioText + Value[Count];
  465. end
  466. else
  467. begin
  468. // Value contain invalid characters
  469. raise EInvalidArgument.CreateFmt(SAspectRatioIsNotValid, [Value]);
  470. end;
  471. end;
  472. YValue := StrToInt(Copy(AspectRatioText, Pos(':', AspectRatioText) + 1,
  473. Length(AspectRatioText)));
  474. end
  475. else
  476. begin
  477. // Value contain invalid characters
  478. raise EInvalidArgument.CreateFmt(SAspectRatioIsNotValid, [Value]);
  479. end;
  480. end;
  481. function RoundUp(AValue:Single):Integer;
  482. var
  483. oRoundMode :TFPURoundingMode;
  484. begin
  485. oRoundMode :=Math.GetRoundMode;
  486. //Round to Upper Value
  487. Math.SetRoundMode(rmUp);
  488. Result :=Round(AValue);
  489. Math.SetRoundMode(oRoundMode);
  490. end;
  491. function ResolutionUnitConvert(const AValue:Single; fromRes, toRes:TResolutionUnit; predefInchRes:Integer):Single;
  492. begin
  493. if (fromRes<>toRes)
  494. then Case fromRes of
  495. ruNone: begin
  496. if toRes=ruPixelsPerInch
  497. then Result :=AValue/predefInchRes //in
  498. else Result :=(AValue/predefInchRes)*2.54; //cm
  499. end;
  500. ruPixelsPerInch :begin
  501. if toRes=ruPixelsPerCentimeter
  502. then Result :=AValue*2.54 //cm
  503. else Result :=AValue*predefInchRes; //pixel
  504. end;
  505. ruPixelsPerCentimeter :begin
  506. if toRes=ruPixelsPerInch
  507. then Result :=AValue/2.54 //in
  508. else Result :=(AValue/2.54)*predefInchRes;//cm
  509. end;
  510. end
  511. else Result:=AValue;
  512. end;
  513. procedure PixelXResolutionUnitConvert(var resX, resY: Single; fromRes, toRes: TResolutionUnit);
  514. begin
  515. //Do Conversion from/to PixelXInch/PixelXCm
  516. if (toRes <> fromRes) then
  517. begin
  518. if (toRes=ruPixelsPerInch)
  519. then begin
  520. resX :=resX*2.54;
  521. resY :=resY*2.54;
  522. end
  523. else begin
  524. resX :=resX/2.54;
  525. resY :=resY/2.54;
  526. end
  527. end;
  528. end;
  529. { TCropArea }
  530. procedure TCropArea.Render_Refresh;
  531. begin
  532. if not(fOwner.rCropAreas.loading) then
  533. begin
  534. fOwner.Render;
  535. fOwner.Refresh;
  536. end;
  537. end;
  538. procedure TCropArea.GetImageResolution(var resX, resY: Single; var resUnit: TResolutionUnit);
  539. begin
  540. resX :=fOwner.fImageBitmap.ResolutionX;
  541. resY :=fOwner.fImageBitmap.ResolutionY;
  542. resUnit :=fOwner.fImageBitmap.ResolutionUnit;
  543. if (resX<2) or (resY<2) then //Some images have 1x1 PixelPerInch ?
  544. begin
  545. //No Resolution use predefined Form Values
  546. resUnit :=rAreaUnit;
  547. if (rAreaUnit=ruPixelsPerInch)
  548. then resX :=fOwner.PixelsPerInch
  549. else resX :=fOwner.PixelsPerInch/2.54;
  550. resY :=resX;
  551. end;
  552. end;
  553. function TCropArea.getIsNullSize: Boolean;
  554. begin
  555. Result := not((abs(rArea.Right - rArea.Left) > 0) and (abs(rArea.Bottom - rArea.Top) > 0));
  556. end;
  557. procedure TCropArea.setName(AValue: String);
  558. begin
  559. if rName=AValue then Exit;
  560. rName:=AValue;
  561. if assigned(fOwner.rOnCropAreaChanged)
  562. then fOwner.rOnCropAreaChanged(fOwner, Self);
  563. end;
  564. procedure TCropArea.setIcons(AValue: TCropAreaIcons);
  565. begin
  566. if rIcons=AValue then Exit;
  567. rIcons:=AValue;
  568. Render_Refresh;
  569. end;
  570. function TCropArea.getTop: Single;
  571. begin
  572. Result :=rArea.Top;
  573. end;
  574. procedure TCropArea.setTop(AValue: Single);
  575. var
  576. tempArea:TRectF;
  577. begin
  578. if AValue=rArea.Top then Exit;
  579. tempArea :=rArea;
  580. tempArea.Top:=AValue;
  581. tempArea.Height:=rArea.Height;
  582. //CheckAreaOutOfBounds(tempArea);
  583. Area :=tempArea;
  584. end;
  585. function TCropArea.getLeft: Single;
  586. begin
  587. Result :=rArea.Left;
  588. end;
  589. procedure TCropArea.setLeft(AValue: Single);
  590. var
  591. tempArea:TRectF;
  592. tempSArea:TRect;
  593. begin
  594. if AValue=rArea.Left then Exit;
  595. tempArea :=rArea;
  596. tempArea.Left:=AValue;
  597. tempArea.Width:=rArea.Width;
  598. //CheckAreaOutOfBounds(tempArea);
  599. Area :=tempArea;
  600. (* if CheckScaledOutOfBounds(rScaledArea)
  601. then begin
  602. CalculateAreaFromScaledArea;
  603. if assigned(fOwner.rOnCropAreaChanged)
  604. then fOwner.rOnCropAreaChanged(fOwner, Self);
  605. end; *)
  606. end;
  607. function TCropArea.getHeight: Single;
  608. begin
  609. Result :=rArea.Height;
  610. end;
  611. procedure TCropArea.setHeight(AValue: Single);
  612. var
  613. tempArea:TRectF;
  614. begin
  615. if AValue=rArea.Height then Exit;
  616. tempArea :=rArea;
  617. tempArea.Height:=AValue;
  618. //CheckAreaOutOfBounds(tempArea);
  619. Area :=tempArea;
  620. end;
  621. function TCropArea.getWidth: Single;
  622. begin
  623. Result :=rArea.Width;
  624. end;
  625. procedure TCropArea.setWidth(AValue: Single);
  626. var
  627. tempArea:TRectF;
  628. begin
  629. if AValue=rArea.Width then Exit;
  630. tempArea :=rArea;
  631. tempArea.Width:=AValue;
  632. //CheckAreaOutOfBounds(tempArea);
  633. Area :=tempArea;
  634. end;
  635. function TCropArea.getMaxHeight: Single;
  636. begin
  637. if (rAreaUnit=ruNone)
  638. then Result :=fOwner.fImageBitmap.Height
  639. else begin
  640. if (fOwner.fImageBitmap.ResolutionY<2)
  641. then Result :=fOwner.fImageBitmap.Height //No Resolution, Some images have 1x1 PixelPerInch ?
  642. else begin
  643. Result :=fOwner.fImageBitmap.ResolutionHeight;
  644. //Do Conversion from/to inch/cm
  645. if (rAreaUnit <> fOwner.fImageBitmap.ResolutionUnit) then
  646. begin
  647. if (rAreaUnit=ruPixelsPerInch)
  648. then Result :=Result/2.54 //Bitmap is in Cm, i'm in Inch
  649. else Result :=Result*2.54; //Bitmap is in Inch, i'm in Cm
  650. end;
  651. end;
  652. end;
  653. end;
  654. function TCropArea.getMaxWidth: Single;
  655. begin
  656. if (rAreaUnit=ruNone)
  657. then Result :=fOwner.fImageBitmap.Width
  658. else begin
  659. if (fOwner.fImageBitmap.ResolutionX<2)
  660. then Result :=fOwner.fImageBitmap.Width //No Resolution, Some images have 1x1 PixelPerInch ?
  661. else begin
  662. Result :=fOwner.fImageBitmap.ResolutionWidth;
  663. //Do Conversion from/to inch/cm
  664. if (rAreaUnit <> fOwner.fImageBitmap.ResolutionUnit) then
  665. begin
  666. if (rAreaUnit=ruPixelsPerInch)
  667. then Result :=Result/2.54 //Bitmap is in Cm, i'm in Inch
  668. else Result :=Result*2.54; //Bitmap is in Inch, i'm in Cm
  669. end;
  670. end;
  671. end;
  672. end;
  673. function TCropArea.getIndex: Longint;
  674. begin
  675. Result :=fOwner.CropAreas.IndexOf(Self);
  676. end;
  677. procedure TCropArea.CalculateScaledAreaFromArea;
  678. var
  679. xRatio, yRatio: Single;
  680. resX, resY: Single;
  681. resUnit:TResolutionUnit;
  682. begin
  683. if not(isNullSize) then
  684. begin
  685. // Calculate Scaled Area given Scale and Resolution
  686. if (fOwner.fImageBitmap.Width=0) or (fOwner.fImageBitmap.Height=0)
  687. then begin
  688. xRatio :=1;
  689. yRatio :=1;
  690. end
  691. else begin
  692. xRatio := fOwner.fResampledBitmap.Width / fOwner.fImageBitmap.Width;
  693. yRatio := fOwner.fResampledBitmap.Height / fOwner.fImageBitmap.Height;
  694. end;
  695. resX :=1; //if rAreaUnit=ruNone use only Ratio
  696. resY :=1;
  697. if (rAreaUnit<>ruNone) then
  698. begin
  699. GetImageResolution(resX, resY, resUnit);
  700. PixelXResolutionUnitConvert(resX, resY, resUnit, rAreaUnit);
  701. end;
  702. //MaxM: Use Trunc for Top/Left and Round for Right/Bottom so we
  703. // preserve as much data as possible when do the crop
  704. rScaledArea.Left := Trunc(rArea.Left * resX * xRatio);
  705. rScaledArea.Top := Trunc(rArea.Top * resY * yRatio);
  706. rScaledArea.Right := Round(rArea.Right* resX * xRatio);
  707. rScaledArea.Bottom := Round(rArea.Bottom * resY * yRatio);
  708. end;
  709. end;
  710. procedure TCropArea.CalculateAreaFromScaledArea;
  711. var
  712. xRatio, yRatio: Single;
  713. resX, resY: Single;
  714. resUnit:TResolutionUnit;
  715. begin
  716. // Calculate Scaled Area given Scale and Resolution
  717. if (fOwner.fImageBitmap.Width=0) or (fOwner.fImageBitmap.Height=0)
  718. then begin
  719. xRatio :=1;
  720. yRatio :=1;
  721. end
  722. else begin
  723. xRatio := fOwner.fResampledBitmap.Width / fOwner.fImageBitmap.Width;
  724. yRatio := fOwner.fResampledBitmap.Height / fOwner.fImageBitmap.Height;
  725. end;
  726. resX :=1; //if rAreaUnit=ruNone use only Ratio
  727. resY :=1;
  728. if (rAreaUnit<>ruNone) then
  729. begin
  730. GetImageResolution(resX, resY, resUnit);
  731. PixelXResolutionUnitConvert(resX, resY, resUnit, rAreaUnit);
  732. end;
  733. rArea.Left := (rScaledArea.Left / resX) / xRatio;
  734. rArea.Right := (rScaledArea.Right / resX) / xRatio;
  735. rArea.Top := (rScaledArea.Top / resY) / yRatio;
  736. rArea.Bottom := (rScaledArea.Bottom / resY) / yRatio;
  737. end;
  738. function TCropArea.GetPixelArea(const AValue: TRectF): TRect;
  739. var
  740. resX, resY: Single;
  741. resUnit: TResolutionUnit;
  742. begin
  743. if (rAreaUnit=ruNone)
  744. then begin
  745. Result.Left := Trunc(AValue.Left);
  746. Result.Right := Trunc(AValue.Right);
  747. Result.Top := Trunc(AValue.Top);
  748. Result.Bottom := Trunc(AValue.Bottom);
  749. end
  750. else begin
  751. if (rAreaUnit=ruNone)
  752. then begin
  753. resX :=1;
  754. resY :=1;
  755. end
  756. else GetImageResolution(resX, resY, resUnit);
  757. PixelXResolutionUnitConvert(resX, resY, resUnit, rAreaUnit);
  758. Result.Left := Trunc(AValue.Left * resX);
  759. Result.Top := Trunc(AValue.Top * resY);
  760. Result.Right := Round(AValue.Right* resX);
  761. Result.Bottom := Round(AValue.Bottom * resY);
  762. end;
  763. end;
  764. function TCropArea.CheckScaledOutOfBounds(var AArea: TRect): Boolean;
  765. var
  766. tmpValue: Integer;
  767. begin
  768. Result :=False;
  769. if (AArea.Left<0)
  770. then begin
  771. tmpValue :=-AArea.Left;
  772. AArea.Left :=0;
  773. AArea.Right:=AArea.Right+tmpValue;
  774. Result :=True;
  775. end;
  776. if (AArea.Top<0)
  777. then begin
  778. tmpValue :=-AArea.Top;
  779. AArea.Top :=0;
  780. AArea.Bottom:=AArea.Bottom+tmpValue;
  781. Result :=True;
  782. end;
  783. if (AArea.Right>fOwner.fResampledBitmap.Width)
  784. then begin
  785. tmpValue :=AArea.Right-fOwner.fResampledBitmap.Width;
  786. AArea.Right :=fOwner.fResampledBitmap.Width;
  787. AArea.Left:=AArea.Left-tmpValue; //if <0 ? a vicious circle
  788. Result :=True;
  789. end;
  790. if (AArea.Bottom>fOwner.fResampledBitmap.Height)
  791. then begin
  792. tmpValue :=AArea.Bottom-fOwner.fResampledBitmap.Height;
  793. AArea.Bottom :=fOwner.fResampledBitmap.Height;
  794. AArea.Top:=AArea.Top-tmpValue; //if <0 ? a vicious circle
  795. Result :=True;
  796. end;
  797. end;
  798. function TCropArea.CheckAreaOutOfBounds(var AArea: TRectF):Boolean;
  799. var
  800. tmpValue, resWH: Single;
  801. begin
  802. Result :=False;
  803. if (AArea.Left<0)
  804. then begin
  805. tmpValue :=-AArea.Left;
  806. AArea.Left :=0;
  807. AArea.Right:=AArea.Right+tmpValue;
  808. Result :=True;
  809. end;
  810. if (AArea.Top<0)
  811. then begin
  812. tmpValue :=-AArea.Top;
  813. AArea.Top :=0;
  814. AArea.Bottom:=AArea.Bottom+tmpValue;
  815. Result :=True;
  816. end;
  817. resWH :=fOwner.fImageBitmap.ResolutionWidth;
  818. if (AArea.Right>resWH)
  819. then begin
  820. tmpValue :=AArea.Right-resWH;
  821. AArea.Right :=resWH;
  822. AArea.Left:=AArea.Left-tmpValue; //if <0 ? a vicious circle
  823. Result :=True;
  824. end;
  825. resWH :=fOwner.fImageBitmap.ResolutionHeight;
  826. if (AArea.Bottom>resWH)
  827. then begin
  828. tmpValue :=AArea.Bottom-resWH;
  829. AArea.Bottom :=resWH;
  830. AArea.Top:=AArea.Top-tmpValue; //if <0 ? a vicious circle
  831. Result :=True;
  832. end;
  833. end;
  834. procedure TCropArea.CopyAspectFromParent;
  835. begin
  836. rAspectX :=fOwner.fAspectX;
  837. rAspectY :=fOwner.fAspectY;
  838. rMinHeight :=fOwner.fMinHeight;
  839. rMinWidth :=fOwner.fMinWidth;
  840. rAspectRatio:=fOwner.fAspectRatio;
  841. rRatio :=fOwner.fRatio;
  842. end;
  843. procedure TCropArea.setAspectRatio(AValue: string);
  844. var
  845. XValue, YValue: integer;
  846. AspectRatioText: string;
  847. fGCD :integer;
  848. begin
  849. if (rKeepAspectRatio = bParent)
  850. then CopyAspectFromParent
  851. else begin
  852. if (AValue <> rAspectRatio) then
  853. begin
  854. // Check if value contain a valid string
  855. CheckAspectRatio(AValue, AspectRatioText, XValue, YValue);
  856. // Set new Aspect Ratio
  857. rAspectRatio := AspectRatioText;
  858. rAspectX := XValue;
  859. rAspectY := YValue;
  860. // Calculate the ratio
  861. fGCD := getGCD(rAspectX, rAspectY);
  862. // Determine the ratio of scale per axle
  863. with rRatio do
  864. begin
  865. Horizontal := rAspectX div fGCD;
  866. Vertical := rAspectY div fGCD;
  867. end;
  868. // Set minimun size
  869. if ((rRatio.Horizontal < fOwner.fAnchorSize + 10) or
  870. (rRatio.Vertical < fOwner.fAnchorSize + 10)) then
  871. begin
  872. rMinWidth := rRatio.Horizontal * 10;
  873. rMinHeight := rRatio.Vertical * 10;
  874. end
  875. else
  876. begin
  877. rMinWidth := rRatio.Horizontal;
  878. rMinHeight := rRatio.Vertical;
  879. end;
  880. fOwner.ApplyRatioToArea(Self);
  881. Render_Refresh;
  882. end;
  883. end;
  884. end;
  885. procedure TCropArea.setKeepAspectRatio(AValue: BoolParent);
  886. begin
  887. if rKeepAspectRatio=AValue then Exit;
  888. rKeepAspectRatio :=AValue;
  889. if (rKeepAspectRatio = bParent) then
  890. begin
  891. rAspectRatio :=fOwner.AspectRatio;
  892. CopyAspectFromParent;
  893. if (fOwner.KeepAspectRatio)
  894. then fOwner.ApplyRatioToArea(Self);
  895. end
  896. else if (rKeepAspectRatio = bTrue)
  897. then fOwner.ApplyRatioToArea(Self);
  898. Render_Refresh;
  899. end;
  900. procedure TCropArea.setArea(AValue: TRectF);
  901. var
  902. curKeepAspectRatio :Boolean;
  903. curRatio :TRatio;
  904. calcHeight, calcWidth, swapV :Single;
  905. begin
  906. if (rArea.TopLeft = AValue.TopLeft) and
  907. (rArea.BottomRight = AValue.BottomRight) then Exit;
  908. if (AValue.Left > AValue.Right) then
  909. begin
  910. swapV :=AValue.Left;
  911. AValue.Left :=AValue.Right;
  912. AValue.Right:=swapV;
  913. end;
  914. if (AValue.Top > AValue.Bottom) then
  915. begin
  916. swapV :=AValue.Top;
  917. AValue.Top :=AValue.Bottom;
  918. AValue.Bottom:=swapV;
  919. end;
  920. if fOwner.fMouseCaught
  921. then rArea:=AValue
  922. else begin
  923. curKeepAspectRatio :=getRealAspectRatio(curRatio);
  924. if curKeepAspectRatio
  925. then begin
  926. calcWidth :=AValue.Width;
  927. calcHeight :=AValue.Height;
  928. //if the Width is Changed recalculate the Height
  929. if (calcWidth <> rArea.Width)
  930. then calcHeight :=Trunc(abs(calcWidth) * (curRatio.Vertical / curRatio.Horizontal))
  931. else begin
  932. //if the New Width is the same but the Height is Changed recalculate the New Width
  933. if (calcHeight <> rArea.Height)
  934. then calcWidth :=Trunc(abs(calcHeight) * (curRatio.Horizontal / curRatio.Vertical));
  935. end;
  936. rArea.Left:=AValue.Left;
  937. rArea.Top:=AValue.Top;
  938. rArea.Width:=calcWidth;
  939. rArea.Height:=calcHeight;
  940. end
  941. else rArea:=AValue; //Free Aspect
  942. CalculateScaledAreaFromArea;
  943. Render_Refresh;
  944. end;
  945. if assigned(fOwner.rOnCropAreaChanged)
  946. then fOwner.rOnCropAreaChanged(fOwner, Self);
  947. end;
  948. procedure TCropArea.setAreaUnit(AValue: TResolutionUnit);
  949. var
  950. imgResX, imgResY :Single;
  951. begin
  952. if rAreaUnit=AValue then Exit;
  953. if not(Loading) and not(isNullSize) then
  954. begin
  955. //Get Image Resolution in Pixel/Inchs
  956. Case fOwner.Bitmap.ResolutionUnit of
  957. ruPixelsPerInch : begin
  958. imgResX :=fOwner.Bitmap.ResolutionX;
  959. imgResY :=fOwner.Bitmap.ResolutionY;
  960. end;
  961. ruPixelsPerCentimeter : begin
  962. imgResX :=fOwner.Bitmap.ResolutionX*2.54;
  963. imgResY :=fOwner.Bitmap.ResolutionY*2.54;
  964. end;
  965. ruNone : begin
  966. //No Image Resolution, Use predefined Monitor Values
  967. imgResX :=fOwner.PixelsPerInch;
  968. imgResY :=fOwner.PixelsPerInch;
  969. end;
  970. end;
  971. //Paranoid test to avoid zero divisions
  972. if (imgResX=0) then imgResX :=fOwner.PixelsPerInch;
  973. if (imgResY=0) then imgResY :=fOwner.PixelsPerInch;
  974. Case rAreaUnit of
  975. ruPixelsPerInch : begin
  976. if (AValue=ruNone)
  977. then begin //From Inchs to Pixels, we need Image Resolution
  978. //MaxM: Use Trunc for Top/Left and Round for Right/Bottom so we
  979. // preserve as much data as possible when do the crop
  980. rArea.Left:=Trunc(rArea.Left*imgResX);
  981. rArea.Top:=Trunc(rArea.Top*imgResY);
  982. rArea.Right:=Round(rArea.Right*imgResX);
  983. rArea.Bottom:=Round(rArea.Bottom*imgResY);
  984. end
  985. else begin //From Inchs to Cm
  986. rArea.Left:=rArea.Left*2.54;
  987. rArea.Top:=rArea.Top*2.54;
  988. rArea.Right:=rArea.Right*2.54;
  989. rArea.Bottom:=rArea.Bottom*2.54;
  990. end;
  991. end;
  992. ruPixelsPerCentimeter : begin
  993. if (AValue=ruNone)
  994. then begin //From Cm to Pixels, first convert to Inchs than use Image Resolution
  995. rArea.Left:=Trunc((rArea.Left/2.54)*imgResX);
  996. rArea.Top:=Trunc((rArea.Top/2.54)*imgResY);
  997. rArea.Right:=Round((rArea.Right/2.54)*imgResX);
  998. rArea.Bottom:=Round((rArea.Bottom/2.54)*imgResY);
  999. end
  1000. else begin //From Cm to Inchs
  1001. rArea.Left:=rArea.Left/2.54;
  1002. rArea.Top:=rArea.Top/2.54;
  1003. rArea.Right:=rArea.Right/2.54;
  1004. rArea.Bottom:=rArea.Bottom/2.54;
  1005. end;
  1006. end;
  1007. ruNone : begin
  1008. if (AValue=ruPixelsPerInch)
  1009. then begin //From Pixels to Inchs
  1010. rArea.Left:=rArea.Left/imgResX;
  1011. rArea.Top:=rArea.Top/imgResY;
  1012. rArea.Right:=rArea.Right/imgResX;
  1013. rArea.Bottom:=rArea.Bottom/imgResY;
  1014. end
  1015. else begin
  1016. rArea.Left:=(rArea.Left/2.54)/imgResX;
  1017. rArea.Top:=(rArea.Top/2.54)/imgResY;
  1018. rArea.Right:=(rArea.Right/2.54)/imgResX;
  1019. rArea.Bottom:=(rArea.Bottom/2.54)/imgResY;
  1020. end;
  1021. end;
  1022. end;
  1023. end;
  1024. rAreaUnit:=AValue;
  1025. if assigned(fOwner.rOnCropAreaChanged)
  1026. then fOwner.rOnCropAreaChanged(fOwner, Self);
  1027. end;
  1028. procedure TCropArea.setScaledArea(AValue: TRect);
  1029. var
  1030. curKeepAspectRatio :Boolean;
  1031. curRatio :TRatio;
  1032. calcHeight, calcWidth, swapV :Longint;
  1033. begin
  1034. if rScaledArea=AValue then Exit;
  1035. if (AValue.Left > AValue.Right) then
  1036. begin
  1037. swapV :=AValue.Left;
  1038. AValue.Left :=AValue.Right;
  1039. AValue.Right:=swapV;
  1040. end;
  1041. if (AValue.Top > AValue.Bottom) then
  1042. begin
  1043. swapV :=AValue.Top;
  1044. AValue.Top :=AValue.Bottom;
  1045. AValue.Bottom:=swapV;
  1046. end;
  1047. if fOwner.fMouseCaught
  1048. then rScaledArea:=AValue
  1049. else begin
  1050. curKeepAspectRatio :=getRealAspectRatio(curRatio);
  1051. if curKeepAspectRatio
  1052. then begin
  1053. calcWidth :=AValue.Width;
  1054. calcHeight :=AValue.Height;
  1055. //if the Width is Changed recalculate the Height
  1056. if (calcWidth <> rScaledArea.Width)
  1057. then calcHeight :=Trunc(abs(calcWidth) * (curRatio.Vertical / curRatio.Horizontal))
  1058. else begin
  1059. //if the New Width is the same but the Height is Changed recalculate the New Width
  1060. if (calcHeight <> rScaledArea.Height)
  1061. then calcWidth :=Trunc(abs(calcHeight) * (curRatio.Horizontal / curRatio.Vertical));
  1062. end;
  1063. rScaledArea.Left:=AValue.Left;
  1064. rScaledArea.Top:=AValue.Top;
  1065. rScaledArea.Width:=calcWidth;
  1066. rScaledArea.Height:=calcHeight;
  1067. end
  1068. else rScaledArea:=AValue; //Free Aspect
  1069. CalculateAreaFromScaledArea;
  1070. Render_Refresh;
  1071. end;
  1072. if assigned(fOwner.rOnCropAreaChanged)
  1073. then fOwner.rOnCropAreaChanged(fOwner, Self);
  1074. end;
  1075. function TCropArea.getRealAspectRatio(var ARatio: TRatio): Boolean;
  1076. begin
  1077. Case rKeepAspectRatio of
  1078. bParent : begin
  1079. Result :=fOwner.fKeepAspectRatio;
  1080. ARatio :=fOwner.fRatio;
  1081. end;
  1082. bTrue : begin
  1083. Result :=True;
  1084. ARatio :=Self.rRatio;
  1085. end;
  1086. bFalse : Result :=False;
  1087. end;
  1088. end;
  1089. function TCropArea.getRealKeepAspectRatio: Boolean;
  1090. begin
  1091. Case rKeepAspectRatio of
  1092. bParent : Result :=fOwner.fKeepAspectRatio;
  1093. bTrue : Result :=True;
  1094. bFalse : Result :=False;
  1095. end;
  1096. end;
  1097. //Get Resampled Bitmap (Scaled to current scale)
  1098. function TCropArea.getResampledBitmap(ACopyProperties: Boolean=False): TBGRABitmap;
  1099. var
  1100. ResampledBitmap: TBGRACustomBitmap;
  1101. CropBitmap: TBGRABitmap;
  1102. begin
  1103. Result :=nil;
  1104. if not (fOwner.fImageBitmap.Empty) then
  1105. try
  1106. try
  1107. // Create a new bitmap for cropped region in original scale
  1108. CropBitmap := getBitmap(ACopyProperties);
  1109. // Create bitmap to put image on final scale
  1110. Result := TBGRABitmap.Create(rScaledArea.Width, rScaledArea.Height);
  1111. // Resize the cropped image to final scale
  1112. ResampledBitmap := CropBitmap.Resample(rScaledArea.Width, rScaledArea.Height, rmFineResample, ACopyProperties);
  1113. Result.BlendImage(0, 0, ResampledBitmap, boLinearBlend);
  1114. finally
  1115. ResampledBitmap.Free;
  1116. CropBitmap.Free;
  1117. end;
  1118. except
  1119. if (Result<>nil)
  1120. then FreeAndNil(Result);
  1121. end;
  1122. end;
  1123. //Get Original size Bitmap (not scaled to current scale)
  1124. function TCropArea.getBitmap(ACopyProperties: Boolean=False): TBGRABitmap;
  1125. begin
  1126. Result :=nil;
  1127. if not (fOwner.fImageBitmap.Empty) then
  1128. try
  1129. // Get the cropped image on selected region in original scale
  1130. Result :=fOwner.fImageBitmap.GetPart(GetPixelArea(rArea), ACopyProperties);
  1131. except
  1132. if (Result<>nil)
  1133. then FreeAndNil(Result);
  1134. end;
  1135. end;
  1136. constructor TCropArea.Create(AOwner: TBGRAImageManipulation; AArea: TRectF;
  1137. AAreaUnit: TResolutionUnit; AUserData: Integer);
  1138. begin
  1139. inherited Create;
  1140. if (AOwner = Nil)
  1141. then raise Exception.Create('TCropArea Owner is Nil');
  1142. OwnerList :=nil;
  1143. fOwner :=AOwner;
  1144. rAreaUnit :=AAreaUnit;
  1145. Area := AArea;
  1146. UserData :=AUserData;
  1147. rAspectX :=3;
  1148. rAspectY :=4;
  1149. rKeepAspectRatio :=bParent;
  1150. Loading:=False;
  1151. CopyAspectFromParent;
  1152. end;
  1153. constructor TCropArea.Create(AOwner: TBGRAImageManipulation;
  1154. DuplicateFrom: TCropArea; InsertInList:Boolean);
  1155. begin
  1156. if (DuplicateFrom = Nil)
  1157. then raise Exception.Create('TCropArea DuplicateFrom is Nil');
  1158. Create(AOwner, DuplicateFrom.Area, DuplicateFrom.AreaUnit, DuplicateFrom.UserData);
  1159. OwnerList :=nil;
  1160. rAspectX :=DuplicateFrom.rAspectX;
  1161. rAspectY :=DuplicateFrom.rAspectY;
  1162. rKeepAspectRatio :=DuplicateFrom.rKeepAspectRatio;
  1163. Loading:=False;
  1164. if rKeepAspectRatio=bParent
  1165. then CopyAspectFromParent;
  1166. if InsertInList and (DuplicateFrom.OwnerList<>nil)
  1167. then DuplicateFrom.OwnerList.add(Self);
  1168. end;
  1169. destructor TCropArea.Destroy;
  1170. begin
  1171. inherited Destroy;
  1172. end;
  1173. procedure TCropArea.BringToFront;
  1174. begin
  1175. if (OwnerList<>nil) then
  1176. try
  1177. OwnerList.Move(OwnerList.IndexOf(Self), OwnerList.Count-1);
  1178. except
  1179. end;
  1180. end;
  1181. procedure TCropArea.BringToBack;
  1182. begin
  1183. if (OwnerList<>nil) then
  1184. try
  1185. OwnerList.Move(OwnerList.IndexOf(Self), 0);
  1186. except
  1187. end;
  1188. end;
  1189. procedure TCropArea.BringForward;
  1190. var
  1191. curIndex :Integer;
  1192. begin
  1193. if (OwnerList<>nil) then
  1194. try
  1195. curIndex :=OwnerList.IndexOf(Self);
  1196. if (curIndex<OwnerList.Count-1)
  1197. then OwnerList.Move(curIndex, curIndex+1);
  1198. except
  1199. end;
  1200. end;
  1201. procedure TCropArea.BringBackward;
  1202. var
  1203. curIndex :Integer;
  1204. begin
  1205. if (OwnerList<>nil) then
  1206. try
  1207. curIndex :=OwnerList.IndexOf(Self);
  1208. if (curIndex>0)
  1209. then OwnerList.Move(curIndex, curIndex-1);
  1210. except
  1211. end;
  1212. end;
  1213. procedure TCropArea.RotateLeft;
  1214. var
  1215. newArea :TRect;
  1216. begin
  1217. newArea.Right :=rScaledArea.Left;
  1218. newArea.Bottom:=rScaledArea.Bottom;
  1219. newArea.Left:=newArea.Right-rScaledArea.Height;
  1220. newArea.Top:=newArea.Bottom-rScaledArea.Width;
  1221. CheckScaledOutOfBounds(newArea);
  1222. ScaledArea :=newArea;
  1223. end;
  1224. procedure TCropArea.RotateRight;
  1225. var
  1226. newArea :TRect;
  1227. begin
  1228. newArea.Left :=rScaledArea.Right;
  1229. newArea.Bottom:=rScaledArea.Bottom;
  1230. newArea.Right:=newArea.Left+rScaledArea.Height;
  1231. newArea.Top:=newArea.Bottom-rScaledArea.Width;
  1232. CheckScaledOutOfBounds(newArea);
  1233. ScaledArea :=newArea;
  1234. end;
  1235. procedure TCropArea.FlipHLeft;
  1236. var
  1237. newArea :TRect;
  1238. begin
  1239. newArea.Top:=rScaledArea.Top;
  1240. newArea.Bottom:=rScaledArea.Bottom;
  1241. newArea.Right :=rScaledArea.Left;
  1242. newArea.Left:=newArea.Right-rScaledArea.Width;
  1243. CheckScaledOutOfBounds(newArea);
  1244. ScaledArea :=newArea;
  1245. end;
  1246. procedure TCropArea.FlipHRight;
  1247. var
  1248. newArea :TRect;
  1249. begin
  1250. newArea.Top:=rScaledArea.Top;
  1251. newArea.Bottom:=rScaledArea.Bottom;
  1252. newArea.Left :=rScaledArea.Right;
  1253. newArea.Right:=newArea.Left+rScaledArea.Width;
  1254. CheckScaledOutOfBounds(newArea);
  1255. ScaledArea :=newArea;
  1256. end;
  1257. procedure TCropArea.FlipVUp;
  1258. var
  1259. newArea :TRect;
  1260. begin
  1261. newArea.Left:=rScaledArea.Left;
  1262. newArea.Right:=rScaledArea.Right;
  1263. newArea.Bottom :=rScaledArea.Top;
  1264. newArea.Top:=newArea.Bottom-rScaledArea.Height;
  1265. CheckScaledOutOfBounds(newArea);
  1266. ScaledArea :=newArea;
  1267. end;
  1268. procedure TCropArea.FlipVDown;
  1269. var
  1270. newArea :TRect;
  1271. begin
  1272. newArea.Left:=rScaledArea.Left;
  1273. newArea.Right:=rScaledArea.Right;
  1274. newArea.Top :=rScaledArea.Bottom;
  1275. newArea.Bottom:=newArea.Top+rScaledArea.Height;
  1276. CheckScaledOutOfBounds(newArea);
  1277. ScaledArea :=newArea;
  1278. end;
  1279. procedure TCropArea.SetSize(AWidth, AHeight: Single);
  1280. var
  1281. tempArea:TRectF;
  1282. begin
  1283. if (AWidth=rArea.Width) and (AHeight=rArea.Height)
  1284. then exit;
  1285. tempArea :=rArea;
  1286. tempArea.Width:=AWidth;
  1287. tempArea.Height:=AHeight;
  1288. //CheckAreaOutOfBounds(tempArea);
  1289. Area :=tempArea;
  1290. end;
  1291. { TCropAreaList }
  1292. procedure TCropAreaList.setLoading(AValue: Boolean);
  1293. var
  1294. i :Integer;
  1295. begin
  1296. for i :=0 to Count-1 do items[i].Loading :=AValue;
  1297. rLoading:=AValue;
  1298. end;
  1299. function TCropAreaList.getCropArea(aIndex: Integer): TCropArea;
  1300. begin
  1301. Result := inherited Items[aIndex] as TCropArea;
  1302. end;
  1303. procedure TCropAreaList.setCropArea(aIndex: Integer; const Value: TCropArea);
  1304. begin
  1305. inherited Items[aIndex] := Value;
  1306. end;
  1307. procedure TCropAreaList.Notify(Ptr: Pointer; Action: TListNotification);
  1308. begin
  1309. Case Action of
  1310. lnAdded: begin
  1311. TCropArea(Ptr).OwnerList :=Self;
  1312. if assigned(fOwner.rOnCropAreaAdded)
  1313. then fOwner.rOnCropAreaAdded(fOwner, Ptr);
  1314. end;
  1315. lnDeleted: begin
  1316. TCropArea(Ptr).OwnerList :=Nil;
  1317. if assigned(fOwner.rOnCropAreaDeleted)
  1318. then fOwner.rOnCropAreaDeleted(fOwner, Ptr);
  1319. end;
  1320. end;
  1321. inherited Notify(Ptr, Action);
  1322. end;
  1323. constructor TCropAreaList.Create(AOwner: TBGRAImageManipulation);
  1324. begin
  1325. inherited Create;
  1326. if (AOwner = Nil)
  1327. then raise Exception.Create('Owner TBGRAImageManipulation is Nil');
  1328. fOwner :=AOwner;
  1329. rName :=Self.ClassName;
  1330. loading :=False;
  1331. end;
  1332. function TCropAreaList.add(aCropArea: TCropArea): integer;
  1333. begin
  1334. Result := inherited Add(aCropArea);
  1335. end;
  1336. procedure TCropAreaList.Load(const XMLConf: TXMLConfig; XMLPath: String);
  1337. var
  1338. i, newCount, newSelected: integer;
  1339. curItemPath, curPath: String;
  1340. newCropArea: TCropArea;
  1341. newArea: TRectF;
  1342. newAreaUnit:TResolutionUnit;
  1343. begin
  1344. try
  1345. if (XMLPath='')
  1346. then curPath :=fOwner.Name+'.'+Self.Name+'/'
  1347. else curPath :=XMLPath+'/';
  1348. newCount := XMLConf.GetValue(curPath+'Count', -1);
  1349. if (newCount=-1)
  1350. then raise Exception.Create('XML Path not Found - '+curPath+'Count');
  1351. Clear;
  1352. Loading :=True;
  1353. newSelected := XMLConf.GetValue(curPath+'Selected', -1);
  1354. for i :=0 to newCount-1 do
  1355. begin
  1356. curItemPath :=curPath+'Item' + IntToStr(i)+'/';
  1357. newArea :=RectF(0,0,0,0);
  1358. //Area
  1359. newArea.Left :=StrToFloat(XMLConf.GetValue(curItemPath+'Area/Left', '0'));
  1360. newArea.Top :=StrToFloat(XMLConf.GetValue(curItemPath+'Area/Top', '0'));
  1361. newArea.Width :=StrToFloat(XMLConf.GetValue(curItemPath+'Area/Width', IntToStr(fOwner.MinWidth)));
  1362. newArea.Height :=StrToFloat(XMLConf.GetValue(curItemPath+'Area/Height', IntToStr(fOwner.MinHeight)));
  1363. newAreaUnit :=TResolutionUnit(XMLConf.GetValue(curItemPath+'AreaUnit', 0));
  1364. newCropArea :=TCropArea.Create(Self.fOwner, newArea, newAreaUnit);
  1365. newCropArea.Loading:=True;
  1366. newCropArea.Name :=XMLConf.GetValue(curItemPath+'Name', 'Name '+IntToStr(i));
  1367. newCropArea.KeepAspectRatio :=BoolParent(XMLConf.GetValue(curItemPath+'KeepAspectRatio', Integer(bParent)));
  1368. newCropArea.AspectRatio :=XMLConf.GetValue(curItemPath+'AspectRatio', '3:4');
  1369. newCropArea.Rotate :=StrToFloat(XMLConf.GetValue(curItemPath+'Rotate', '0'));
  1370. newCropArea.UserData :=XMLConf.GetValue(curItemPath+'UserData', -1);
  1371. if assigned(fOwner.rOnCropAreaLoad)
  1372. then newCropArea.UserData :=fOwner.rOnCropAreaLoad(fOwner, newCropArea, XMLConf, curItemPath);
  1373. newCropArea.Loading:=False;
  1374. add(newCropArea);
  1375. end;
  1376. if (newCount>0)
  1377. then begin
  1378. if (newSelected<newCount)
  1379. then fOwner.SelectedCropArea :=items[newSelected]
  1380. else fOwner.SelectedCropArea :=items[0];
  1381. end
  1382. else fOwner.SelectedCropArea :=nil;
  1383. finally
  1384. loading :=False;
  1385. fOwner.Render;
  1386. fOwner.Refresh;
  1387. end;
  1388. end;
  1389. procedure TCropAreaList.Save(const XMLConf: TXMLConfig; XMLPath: String);
  1390. var
  1391. i: integer;
  1392. curItemPath, curPath: String;
  1393. curCropArea: TCropArea;
  1394. begin
  1395. if (XMLPath='')
  1396. then curPath :=fOwner.Name+'.'+Self.Name+'/'
  1397. else curPath :=XMLPath+'/';
  1398. XMLConf.DeletePath(curPath);
  1399. XMLConf.SetValue(curPath+'Count', Count);
  1400. if (fOwner.SelectedCropArea<>nil)
  1401. then XMLConf.SetValue(curPath+'Selected', fOwner.SelectedCropArea.Index)
  1402. else XMLConf.SetValue(curPath+'Selected', -1);
  1403. for i :=0 to Count-1 do
  1404. begin
  1405. curItemPath :=curPath+'Item' + IntToStr(i)+'/';
  1406. curCropArea:=Items[i];
  1407. XMLConf.SetValue(curItemPath+'Name', curCropArea.Name);
  1408. XMLConf.SetValue(curItemPath+'KeepAspectRatio', Integer(curCropArea.KeepAspectRatio));
  1409. XMLConf.SetValue(curItemPath+'AspectRatio', curCropArea.AspectRatio);
  1410. XMLConf.SetValue(curItemPath+'Rotate', FloatToStr(curCropArea.Rotate));
  1411. XMLConf.SetValue(curItemPath+'AreaUnit', Integer(curCropArea.AreaUnit));
  1412. XMLConf.SetValue(curItemPath+'UserData', curCropArea.UserData);
  1413. //Area
  1414. XMLConf.SetValue(curItemPath+'Area/Left', FloatToStr(curCropArea.Area.Left));
  1415. XMLConf.SetValue(curItemPath+'Area/Top', FloatToStr(curCropArea.Area.Top));
  1416. XMLConf.SetValue(curItemPath+'Area/Width', FloatToStr(curCropArea.Area.Width));
  1417. XMLConf.SetValue(curItemPath+'Area/Height', FloatToStr(curCropArea.Area.Height));
  1418. if assigned(fOwner.rOnCropAreaSave)
  1419. then fOwner.rOnCropAreaSave(fOwner, curCropArea, XMLConf, curItemPath);
  1420. end;
  1421. end;
  1422. procedure TCropAreaList.LoadFromStream(Stream: TStream; XMLPath: String);
  1423. var
  1424. FXMLConf: TXMLConfig;
  1425. begin
  1426. try
  1427. FXMLConf := TXMLConfig.Create(nil);
  1428. {$IFDEF USE_Laz2_XMLCfg}
  1429. FXMLConf.ReadFromStream(Stream);
  1430. {$ELSE}
  1431. FXMLConf.ReadOnly:=True;
  1432. FXMLConf.LoadFromStream(Stream);
  1433. {$ENDIF}
  1434. Load(FXMLConf, XMLPath);
  1435. finally
  1436. FXMLConf.Free;
  1437. end;
  1438. end;
  1439. procedure TCropAreaList.LoadFromFile(const FileName: String; XMLPath: String);
  1440. var
  1441. FXMLConf: TXMLConfig;
  1442. begin
  1443. try
  1444. {$IFDEF USE_Laz2_XMLCfg}
  1445. FXMLConf := TXMLConfig.Create(FileName);
  1446. {$ELSE}
  1447. FXMLConf := TXMLConfig.Create(nil);
  1448. FXMLConf.ReadOnly:=True;
  1449. FXMLConf.LoadFromFile(FileName);
  1450. {$ENDIF}
  1451. Load(FXMLConf, XMLPath);
  1452. finally
  1453. FXMLConf.Free;
  1454. end;
  1455. end;
  1456. procedure TCropAreaList.SaveToStream(Stream: TStream; XMLPath: String);
  1457. var
  1458. FXMLConf: TXMLConfig;
  1459. begin
  1460. try
  1461. FXMLConf := TXMLConfig.Create(nil);
  1462. Save(FXMLConf, XMLPath);
  1463. {$IFDEF USE_Laz2_XMLCfg}
  1464. FXMLConf.WriteToStream(Stream);
  1465. {$ELSE}
  1466. FXMLConf.SaveToStream(Stream);
  1467. {$ENDIF}
  1468. finally
  1469. FXMLConf.Free;
  1470. end;
  1471. end;
  1472. procedure TCropAreaList.SaveToFile(const FileName: String; XMLPath: String);
  1473. var
  1474. FXMLConf: TXMLConfig;
  1475. begin
  1476. try
  1477. {$IFDEF USE_Laz2_XMLCfg}
  1478. FXMLConf := TXMLConfig.Create(FileName);
  1479. Save(FXMLConf, XMLPath);
  1480. FXMLConf.Flush;
  1481. {$ELSE}
  1482. FXMLConf := TXMLConfig.Create(nil);
  1483. Save(FXMLConf, XMLPath);
  1484. FXMLConf.SaveToFile(FileName);
  1485. {$ENDIF}
  1486. finally
  1487. FXMLConf.Free;
  1488. end;
  1489. end;
  1490. procedure TCropAreaList.RotateLeft;
  1491. var
  1492. i :Integer;
  1493. begin
  1494. for i:=0 to Count-1 do Items[i].RotateLeft;
  1495. end;
  1496. procedure TCropAreaList.RotateRight;
  1497. var
  1498. i :Integer;
  1499. begin
  1500. for i:=0 to Count-1 do Items[i].RotateRight;
  1501. end;
  1502. procedure TCropAreaList.FlipHLeft;
  1503. var
  1504. i :Integer;
  1505. begin
  1506. for i:=0 to Count-1 do Items[i].FlipHLeft;
  1507. end;
  1508. procedure TCropAreaList.FlipHRight;
  1509. var
  1510. i :Integer;
  1511. begin
  1512. for i:=0 to Count-1 do Items[i].FlipHRight;
  1513. end;
  1514. procedure TCropAreaList.FlipVUp;
  1515. var
  1516. i :Integer;
  1517. begin
  1518. for i:=0 to Count-1 do Items[i].FlipVUp;
  1519. end;
  1520. procedure TCropAreaList.FlipVDown;
  1521. var
  1522. i :Integer;
  1523. begin
  1524. for i:=0 to Count-1 do Items[i].FlipVDown;
  1525. end;
  1526. { TBGRAEmptyImage }
  1527. function TBGRAEmptyImage.getHeight: Integer;
  1528. var
  1529. wRect: TRect;
  1530. begin
  1531. if (rResolutionHeight<=0) or (rResolutionWidth<=0)
  1532. then begin
  1533. //wRect := fOwner.getWorkRect;
  1534. wRect := fOwner.GetClientRect;
  1535. InflateRect(wRect, -fOwner.BorderSize, -fOwner.BorderSize);
  1536. Result := wRect.Bottom-wRect.Top;
  1537. end
  1538. else Case rResolutionUnit of
  1539. ruNone : Result :=Trunc(rResolutionHeight);
  1540. ruPixelsPerInch : Result :=Round(fOwner.PixelsPerInch*rResolutionHeight);
  1541. ruPixelsPerCentimeter : Result :=Round((fOwner.PixelsPerInch/2.54)*rResolutionHeight);
  1542. end;
  1543. end;
  1544. function TBGRAEmptyImage.getWidth: Integer;
  1545. var
  1546. wRect: TRect;
  1547. begin
  1548. if (rResolutionWidth<=0) or (rResolutionHeight<=0)
  1549. then begin
  1550. //wRect := fOwner.getWorkRect;
  1551. wRect := fOwner.GetClientRect;
  1552. InflateRect(wRect, -fOwner.BorderSize, -fOwner.BorderSize);
  1553. Result := wRect.Right-wRect.Left;
  1554. end
  1555. else Case rResolutionUnit of
  1556. ruNone : Result :=Trunc(rResolutionWidth);
  1557. ruPixelsPerInch : Result :=Round(fOwner.PixelsPerInch*rResolutionWidth);
  1558. ruPixelsPerCentimeter : Result :=Round((fOwner.PixelsPerInch/2.54)*rResolutionWidth);
  1559. end;
  1560. end;
  1561. procedure TBGRAEmptyImage.SetResolutionUnit(AValue: TResolutionUnit);
  1562. begin
  1563. if (AValue<>rResolutionUnit) then
  1564. begin
  1565. rResolutionWidth :=ResolutionUnitConvert(rResolutionWidth, rResolutionUnit, AValue, fOwner.PixelsPerInch);
  1566. rResolutionHeight :=ResolutionUnitConvert(rResolutionHeight, rResolutionUnit, AValue, fOwner.PixelsPerInch);
  1567. rResolutionUnit :=AValue;
  1568. end;
  1569. end;
  1570. constructor TBGRAEmptyImage.Create(AOwner: TBGRAImageManipulation);
  1571. begin
  1572. inherited Create;
  1573. fOwner :=AOwner;
  1574. rShowBorder :=False;
  1575. rResolutionUnit:=ruPixelsPerCentimeter;
  1576. end;
  1577. { TBGRANewCropAreaDefault }
  1578. constructor TBGRANewCropAreaDefault.Create(AOwner: TBGRAImageManipulation);
  1579. begin
  1580. inherited Create;
  1581. fOwner :=AOwner;
  1582. rKeepAspectRatio:=bFalse;
  1583. rAspectRatio:='3:4';
  1584. rResolutionUnit:=ruPixelsPerCentimeter;
  1585. rIcons:= [];
  1586. end;
  1587. procedure TBGRANewCropAreaDefault.CopyPropertiesToArea(ANewArea: TCropArea);
  1588. begin
  1589. ANewArea.rIcons:= Self.rIcons;
  1590. ANewArea.rAspectRatio:= Self.rAspectRatio;
  1591. ANewArea.KeepAspectRatio:= Self.rKeepAspectRatio;
  1592. end;
  1593. { TBGRAImageManipulation }
  1594. { ============================================================================ }
  1595. { =====[ Auxiliary Functions ]================================================ }
  1596. { ============================================================================ }
  1597. { Applies the given size constraint on the coordinates along both axes }
  1598. function TBGRAImageManipulation.ApplyDimRestriction(Coords: TCoord;
  1599. Direction: TDirection; Bounds: TRect; AKeepAspectRatio:Boolean): TCoord;
  1600. var
  1601. newCoords: TCoord;
  1602. calcWidth, calcHeight: integer;
  1603. recalculateHeight: boolean;
  1604. begin
  1605. // Gets coordinates
  1606. newCoords := Coords;
  1607. recalculateHeight := False;
  1608. // Calculated width
  1609. calcWidth := abs(newCoords.x2 - newCoords.x1);
  1610. calcHeight := abs(newCoords.y2 - newCoords.y1);
  1611. // Checks if the width is smaller than the minimum value
  1612. if (Abs(calcWidth) < MinWidth) and (MinWidth < fImageBitmap.Width) then
  1613. begin
  1614. // Resizes the width based on the minimum value
  1615. calcWidth := MinWidth;
  1616. if (EAST in Direction) then
  1617. begin
  1618. // If the motion is in a positive direction, make sure we're not going out
  1619. // of bounds
  1620. if ((newCoords.x1 + calcWidth) > Bounds.Right) then
  1621. begin
  1622. // Moves the horizontal coordinates
  1623. newCoords.x1 := Bounds.Right - calcWidth;
  1624. newCoords.x2 := Bounds.Right;
  1625. end
  1626. else
  1627. begin
  1628. // Moves the last horizontal coordinate
  1629. newCoords.x2 := newCoords.x1 + calcWidth;
  1630. end;
  1631. end
  1632. else
  1633. begin
  1634. // If the motion is in a negative direction, make sure we're not going out
  1635. // of bounds
  1636. if ((newCoords.x1 - calcWidth) < Bounds.Left) then
  1637. begin
  1638. // Moves the horizontal coordinates
  1639. newCoords.x1 := Bounds.Left + calcWidth;
  1640. newCoords.x2 := Bounds.Left;
  1641. end
  1642. else
  1643. begin
  1644. // Moves the last horizontal coordinate
  1645. newCoords.x2 := newCoords.x1 - calcWidth;
  1646. end;
  1647. end;
  1648. if (AKeepAspectRatio) then
  1649. begin
  1650. // Resizes the height based on the minimum value
  1651. recalculateHeight := True;
  1652. end;
  1653. end;
  1654. // Checks if the height is smaller than the minimum value
  1655. if (((Abs(calcHeight) < MinHeight) and (MinHeight < fImageBitmap.Height)) or
  1656. recalculateHeight) then
  1657. begin
  1658. // Resizes the height based on the minimum value
  1659. calcHeight := MinHeight;
  1660. if (SOUTH in Direction) then
  1661. begin
  1662. // If the motion is in a positive direction, make sure we're not going out
  1663. // of bounds
  1664. if ((newCoords.y1 + calcHeight) > Bounds.Bottom) then
  1665. begin
  1666. // Moves the vertical coordinates
  1667. newCoords.y1 := Bounds.Bottom - calcHeight;
  1668. newCoords.y2 := Bounds.Bottom;
  1669. end
  1670. else
  1671. begin
  1672. // Moves the last horizontal coordinate
  1673. newCoords.y2 := newCoords.y1 + calcHeight;
  1674. end;
  1675. end
  1676. else
  1677. begin
  1678. // If the motion is in a negative direction, make sure we're not going out
  1679. // of bounds
  1680. if ((newCoords.y1 - calcHeight) < Bounds.Top) then
  1681. begin
  1682. // Moves the vertical coordinates
  1683. newCoords.y1 := Bounds.Top + calcHeight;
  1684. newCoords.y2 := Bounds.Top;
  1685. end
  1686. else
  1687. begin
  1688. // Moves the last horizontal coordinate
  1689. newCoords.y2 := newCoords.y1 - calcHeight;
  1690. end;
  1691. end;
  1692. end;
  1693. Result := newCoords;
  1694. end;
  1695. { Applies the provided ratio to the coordinates based on direction and bounds }
  1696. { on both axes. }
  1697. function TBGRAImageManipulation.ApplyRatioToAxes(Coords: TCoord;
  1698. Direction: TDirection; Bounds: TRect; ACropArea :TCropArea = Nil): TCoord;
  1699. var
  1700. newCoords: TCoord;
  1701. calcWidth, calcHeight: integer;
  1702. RecalculatesOtherAxis,
  1703. curKeepAspectRatio :Boolean;
  1704. curRatio :TRatio;
  1705. begin
  1706. // Gets coordinates
  1707. newCoords := Coords;
  1708. if (ACropArea<>nil)
  1709. then curKeepAspectRatio :=ACropArea.getRealAspectRatio(curRatio)
  1710. else begin
  1711. curKeepAspectRatio :=Self.fKeepAspectRatio;
  1712. curRatio :=Self.fRatio;
  1713. end;
  1714. // Check if movement is only vertical
  1715. if ((fAnchorSelected = [NORTH]) or (fAnchorSelected = [SOUTH])) then
  1716. begin
  1717. // Vertical movement: keep current width
  1718. if (curKeepAspectRatio) then
  1719. begin
  1720. // Calculate height
  1721. calcHeight := newCoords.y2 - newCoords.y1;
  1722. // Make sure we're not going out of bounds
  1723. if (SOUTH in Direction) then
  1724. begin
  1725. if ((newCoords.y1 + calcHeight) > Bounds.Bottom) then
  1726. begin
  1727. calcHeight := Bounds.Bottom - newCoords.y1; // Limite height dimension
  1728. newCoords.y2 := Bounds.Bottom;
  1729. end;
  1730. end
  1731. else
  1732. begin
  1733. if ((newCoords.y1 + calcHeight) < Bounds.Top) then
  1734. begin
  1735. calcHeight := -(newCoords.y1 - Bounds.Top); // Limite height dimension
  1736. newCoords.y2 := Bounds.Top;
  1737. end;
  1738. end;
  1739. // Calculate the new width based on the proportion of height
  1740. calcWidth := Trunc(abs(calcHeight) * (curRatio.Horizontal / curRatio.Vertical));
  1741. // Make sure we're not going out of bounds
  1742. if (fAnchorSelected = [NORTH]) then
  1743. begin
  1744. if ((newCoords.x1 - calcWidth) < Bounds.Left) then
  1745. begin
  1746. calcWidth := newCoords.x1 - Bounds.Left; // Limite width dimension
  1747. newCoords.x2 := Bounds.Left;
  1748. RecalculatesOtherAxis := True;
  1749. end;
  1750. end
  1751. else
  1752. begin
  1753. if ((newCoords.x1 + calcWidth) > Bounds.Right) then
  1754. begin
  1755. calcWidth := Bounds.Right - newCoords.x1; // Limite width dimension
  1756. newCoords.x2 := Bounds.Right;
  1757. RecalculatesOtherAxis := True;
  1758. end;
  1759. end;
  1760. // Apply calculated dimensions of width on height
  1761. if {%H-}(RecalculatesOtherAxis) then
  1762. begin
  1763. if (calcHeight > 0) then
  1764. calcHeight := Trunc(calcWidth * (curRatio.Vertical / curRatio.Horizontal))
  1765. else
  1766. calcHeight := -Trunc(calcWidth * (curRatio.Vertical / curRatio.Horizontal));
  1767. newCoords.y2 := newCoords.y1 + calcHeight;
  1768. end;
  1769. end
  1770. else
  1771. begin
  1772. // Calculate height
  1773. calcHeight := newCoords.y2 - newCoords.y1;
  1774. // Make sure we're not going out of bounds
  1775. if (SOUTH in Direction) then
  1776. begin
  1777. if ((newCoords.y1 + calcHeight) > Bounds.Bottom) then
  1778. begin
  1779. calcHeight := Bounds.Bottom - newCoords.y1; // Limite height dimension
  1780. newCoords.y2 := Bounds.Bottom;
  1781. end;
  1782. end
  1783. else
  1784. begin
  1785. if ((newCoords.y1 + calcHeight) < Bounds.Top) then
  1786. begin
  1787. calcHeight := -(newCoords.y1 - Bounds.Top); // Limite height dimension
  1788. newCoords.y2 := Bounds.Top;
  1789. end;
  1790. end;
  1791. // Calculate width
  1792. if (ACropArea <> Nil)
  1793. then calcWidth := abs(ACropArea.ScaledArea.Right - ACropArea.ScaledArea.Left)
  1794. else calcWidth := 16; //Check
  1795. end;
  1796. if (fAnchorSelected = [NORTH]) then
  1797. newCoords.x2 := newCoords.x1 - calcWidth
  1798. else
  1799. newCoords.x2 := newCoords.x1 + calcWidth;
  1800. end
  1801. else
  1802. // Check if movement is only horizontal
  1803. if ((fAnchorSelected = [EAST]) or (fAnchorSelected = [WEST])) then
  1804. begin
  1805. // Horizontal movement: keep current height
  1806. if (curKeepAspectRatio) then
  1807. begin
  1808. // Calculate width
  1809. calcWidth := newCoords.x2 - newCoords.x1;
  1810. // Make sure we're not going out of bounds
  1811. if (EAST in Direction) then
  1812. begin
  1813. if ((newCoords.x1 + calcWidth) > Bounds.Right) then
  1814. begin
  1815. calcWidth := Bounds.Right - newCoords.x1; // Limite width dimension
  1816. newCoords.x2 := Bounds.Right;
  1817. end;
  1818. end;
  1819. if (WEST in Direction) then
  1820. begin
  1821. if ((newCoords.x1 + calcWidth) < Bounds.Left) then
  1822. begin
  1823. calcWidth := -(newCoords.x1 - Bounds.Left); // Limite width dimension
  1824. newCoords.x2 := Bounds.Left;
  1825. end;
  1826. end;
  1827. // Calculate the new height based on the proportion of width
  1828. calcHeight := Trunc(abs(calcWidth) * (curRatio.Vertical / curRatio.Horizontal));
  1829. // Make sure we're not going out of bounds
  1830. if (fAnchorSelected = [WEST]) then
  1831. begin
  1832. if ((newCoords.y1 - calcHeight) < Bounds.Top) then
  1833. begin
  1834. calcHeight := newCoords.y1 - Bounds.Top; // Limite height dimension
  1835. newCoords.y2 := Bounds.Top;
  1836. RecalculatesOtherAxis := True;
  1837. end;
  1838. end
  1839. else
  1840. begin
  1841. if ((newCoords.y1 + calcHeight) > Bounds.Bottom) then
  1842. begin
  1843. calcHeight := Bounds.Bottom - newCoords.y1; // Limite height dimension
  1844. newCoords.y2 := Bounds.Bottom;
  1845. RecalculatesOtherAxis := True;
  1846. end;
  1847. end;
  1848. // Apply calculated dimensions of height on width
  1849. if (RecalculatesOtherAxis) then
  1850. begin
  1851. if (calcWidth > 0) then
  1852. calcWidth := Trunc(calcHeight * (curRatio.Horizontal / curRatio.Vertical))
  1853. else
  1854. calcWidth := -Trunc(calcHeight * (curRatio.Horizontal / curRatio.Vertical));
  1855. newCoords.x2 := newCoords.x1 + calcWidth;
  1856. end;
  1857. end
  1858. else
  1859. begin
  1860. // Calculate width
  1861. calcWidth := newCoords.x2 - newCoords.x1;
  1862. // Make sure we're not going out of bounds
  1863. if (EAST in Direction) then
  1864. begin
  1865. if ((newCoords.x1 + calcWidth) > Bounds.Right) then
  1866. begin
  1867. calcWidth := Bounds.Right - newCoords.x1; // Limite width dimension
  1868. newCoords.x2 := Bounds.Right;
  1869. end;
  1870. end;
  1871. if (WEST in Direction) then
  1872. begin
  1873. if ((newCoords.x1 + calcWidth) < Bounds.Left) then
  1874. begin
  1875. calcWidth := -(newCoords.x1 - Bounds.Left); // Limite width dimension
  1876. newCoords.x2 := Bounds.Left;
  1877. end;
  1878. end;
  1879. // Calculate height
  1880. if (ACropArea <> Nil)
  1881. then calcHeight := abs(ACropArea.ScaledArea.Bottom - ACropArea.ScaledArea.Top)
  1882. else calcHeight := 16; //Check
  1883. end;
  1884. if (fAnchorSelected = [WEST]) then
  1885. newCoords.y2 := newCoords.y1 - calcHeight
  1886. else
  1887. newCoords.y2 := newCoords.y1 + calcHeight;
  1888. end
  1889. else
  1890. begin
  1891. // Diagonal movement
  1892. if (curKeepAspectRatio) then
  1893. begin
  1894. // Calculate width
  1895. calcWidth := newCoords.x2 - newCoords.x1;
  1896. // Make sure we're not going out of bounds
  1897. if (EAST in Direction) then
  1898. begin
  1899. if ((newCoords.x1 + calcWidth) > Bounds.Right) then
  1900. begin
  1901. calcWidth := Bounds.Right - newCoords.x1; // Limite width dimension
  1902. newCoords.x2 := Bounds.Right;
  1903. end;
  1904. end;
  1905. if (WEST in Direction) then
  1906. begin
  1907. if ((newCoords.x1 + calcWidth) < Bounds.Left) then
  1908. begin
  1909. calcWidth := -(newCoords.x1 - Bounds.Left); // Limite width dimension
  1910. newCoords.x2 := Bounds.Left;
  1911. end;
  1912. end;
  1913. // Calculate the new height based on the proportion of width
  1914. if ((newCoords.y2 - newCoords.y1) > 0) then
  1915. calcHeight := Trunc(abs(calcWidth) * (curRatio.Vertical / curRatio.Horizontal))
  1916. else
  1917. calcHeight := -Trunc(abs(calcWidth) * (curRatio.Vertical / curRatio.Horizontal));
  1918. // Make sure we're not going out of bounds
  1919. if (calcHeight > 0) then
  1920. begin
  1921. if (SOUTH in Direction) then
  1922. begin
  1923. if ((newCoords.y1 + calcHeight) > Bounds.Bottom) then
  1924. begin
  1925. calcHeight := Bounds.Bottom - newCoords.y1; // Limite height dimension
  1926. newCoords.y2 := Bounds.Bottom;
  1927. RecalculatesOtherAxis := True;
  1928. end;
  1929. end
  1930. else
  1931. begin
  1932. if ((newCoords.y1 - calcHeight) < Bounds.Top) then
  1933. begin
  1934. calcHeight := newCoords.y1 - Bounds.Top; // Limite height dimension
  1935. newCoords.y2 := Bounds.Top;
  1936. RecalculatesOtherAxis := True;
  1937. end;
  1938. end;
  1939. end
  1940. else
  1941. begin
  1942. if (SOUTH in Direction) then
  1943. begin
  1944. if ((newCoords.y1 - calcHeight) > Bounds.Bottom) then
  1945. begin
  1946. calcHeight := newCoords.y1 - Bounds.Bottom; // Limite height dimension
  1947. newCoords.y2 := Bounds.Bottom;
  1948. RecalculatesOtherAxis := True;
  1949. end;
  1950. end
  1951. else
  1952. begin
  1953. if ((newCoords.y1 + calcHeight) < Bounds.Top) then
  1954. begin
  1955. calcHeight := Bounds.Top - newCoords.y1; // Limite height dimension
  1956. newCoords.y2 := Bounds.Top;
  1957. RecalculatesOtherAxis := True;
  1958. end;
  1959. end;
  1960. end;
  1961. // Apply calculated dimensions of height on width
  1962. if (RecalculatesOtherAxis) then
  1963. begin
  1964. if (calcWidth > 0) then
  1965. calcWidth := Trunc(abs(calcHeight) * (curRatio.Horizontal / curRatio.Vertical))
  1966. else
  1967. calcWidth := -Trunc(abs(calcHeight) * (curRatio.Horizontal / curRatio.Vertical));
  1968. newCoords.x2 := newCoords.x1 + calcWidth;
  1969. end;
  1970. end
  1971. else
  1972. begin
  1973. // Calculate width
  1974. calcWidth := newCoords.x2 - newCoords.x1;
  1975. // Make sure we're not going out of bounds
  1976. if (EAST in Direction) then
  1977. begin
  1978. if ((newCoords.x1 + calcWidth) > Bounds.Right) then
  1979. begin
  1980. calcWidth := Bounds.Right - newCoords.x1; // Limite width dimension
  1981. newCoords.x2 := Bounds.Right;
  1982. end;
  1983. end;
  1984. if (WEST in Direction) then
  1985. begin
  1986. if ((newCoords.x1 + calcWidth) < Bounds.Left) then
  1987. begin
  1988. calcWidth := -(newCoords.x1 - Bounds.Left); // Limite width dimension
  1989. newCoords.x2 := Bounds.Left;
  1990. end;
  1991. end;
  1992. // Calculate height
  1993. calcHeight := newCoords.y2 - newCoords.y1;
  1994. // Make sure we're not going out of bounds
  1995. if (SOUTH in Direction) then
  1996. begin
  1997. if ((newCoords.y1 + calcHeight) > Bounds.Bottom) then
  1998. begin
  1999. calcHeight := Bounds.Bottom - newCoords.y1; // Limite height dimension
  2000. newCoords.y2 := Bounds.Bottom;
  2001. end;
  2002. end;
  2003. if (NORTH in Direction) then
  2004. begin
  2005. if ((newCoords.y1 + calcHeight) < Bounds.Top) then
  2006. begin
  2007. calcHeight := -(newCoords.y1 - Bounds.Top); // Limite height dimension
  2008. newCoords.y2 := Bounds.Top;
  2009. end;
  2010. end;
  2011. end;
  2012. newCoords.x2 := newCoords.x1 + calcWidth;
  2013. newCoords.y2 := newCoords.y1 + calcHeight;
  2014. end;
  2015. Result := newCoords;
  2016. end;
  2017. procedure TBGRAImageManipulation.ApplyRatioToArea(ACropArea :TCropArea);
  2018. var
  2019. calcWidth, calcHeight :Integer;
  2020. CropAreaRect, Bounds :TRect;
  2021. curRatio :TRatio;
  2022. curKeepAspectRatio :Boolean;
  2023. begin
  2024. if (ACropArea <> Nil) then
  2025. begin
  2026. CropAreaRect :=ACropArea.ScaledArea;
  2027. Bounds := getImageRect(fResampledBitmap);
  2028. // Calculate width
  2029. calcWidth :=CropAreaRect.Right-CropAreaRect.Left;
  2030. // Make sure we're not going out of bounds with Widht
  2031. if ((CropAreaRect.Left+calcWidth)>Bounds.Right) then
  2032. begin
  2033. calcWidth :=Bounds.Right-CropAreaRect.Left; // Limite width dimension
  2034. CropAreaRect.Right :=Bounds.Right;
  2035. end;
  2036. curKeepAspectRatio :=ACropArea.getRealAspectRatio(curRatio);
  2037. if curKeepAspectRatio // Calculate the new height based on the proportion of width
  2038. then calcHeight := Trunc(abs(calcWidth)*(curRatio.Vertical/curRatio.Horizontal));
  2039. //else calcHeight := CropAreaRect.Height; //Raise an Exception ???
  2040. // Make sure we're not going out of bounds with Height
  2041. if ((CropAreaRect.Top+calcHeight) > Bounds.Bottom) then
  2042. begin
  2043. calcHeight :=Bounds.Bottom-CropAreaRect.Top;
  2044. calcWidth :=Trunc(abs(calcHeight)*(curRatio.Horizontal/curRatio.Vertical));
  2045. end;
  2046. CropAreaRect.Right :=CropAreaRect.Left+calcWidth;
  2047. CropAreaRect.Bottom :=CropAreaRect.Top+calcHeight;
  2048. ACropArea.ScaledArea :=CropAreaRect;
  2049. end;
  2050. end;
  2051. { Calculate the maximun selection allowed }
  2052. procedure TBGRAImageManipulation.CalcMaxSelection(ACropArea :TCropArea);
  2053. var
  2054. ImageRect: TRect;
  2055. newCoords: TCoord;
  2056. Direction: TDirection;
  2057. Bounds: TRect;
  2058. begin
  2059. if (ACropArea <> Nil) then
  2060. begin
  2061. ImageRect := getImageRect(fImageBitmap);
  2062. // Initiates coord
  2063. with newCoords do
  2064. begin
  2065. x1 := 0;
  2066. y1 := 0;
  2067. x2 := ImageRect.Right - ImageRect.Left;
  2068. y2 := ImageRect.Bottom - ImageRect.Top;
  2069. end;
  2070. // Determine direction
  2071. Direction := getDirection(Point(newCoords.x1, newCoords.y1),
  2072. Point(newCoords.x2, newCoords.y2));
  2073. // Determines limite values
  2074. with newCoords do
  2075. begin
  2076. x1 := 0;
  2077. y1 := 0;
  2078. x2 := ImageRect.Right - ImageRect.Left;
  2079. y2 := ImageRect.Bottom - ImageRect.Top;
  2080. end;
  2081. Bounds := getImageRect(fResampledBitmap);
  2082. // Apply the ratio
  2083. newCoords := ApplyRatioToAxes(newCoords, Direction, Bounds);
  2084. // Determines minimum value on both axes
  2085. newCoords := ApplyDimRestriction(newCoords, Direction, Bounds, fKeepAspectRatio);
  2086. ACropArea.ScaledArea := Rect(newCoords.x1, newCoords.y1, newCoords.x2, newCoords.y2);
  2087. end;
  2088. end;
  2089. { Calculate the Aspect Ratio for size limits}
  2090. procedure TBGRAImageManipulation.findSizeLimits;
  2091. var
  2092. WorkRect: TRect;
  2093. begin
  2094. // Find the working area of the component
  2095. WorkRect := getWorkRect;
  2096. with fSizeLimits do
  2097. begin
  2098. minWidth := fAspectX;
  2099. maxWidth := WorkRect.Right - WorkRect.Left;
  2100. minHeight := fAspectY;
  2101. maxHeight := WorkRect.Bottom - WorkRect.Top;
  2102. end;
  2103. end;
  2104. { Get the direction of movement }
  2105. function TBGRAImageManipulation.getDirection(const Point1, Point2: TPoint): TDirection;
  2106. begin
  2107. Result := [];
  2108. if (Point1.X > Point2.X) then
  2109. Result := Result + [WEST];
  2110. if (Point1.X < Point2.X) then
  2111. Result := Result + [EAST];
  2112. if (Point1.Y > Point2.Y) then
  2113. Result := Result + [NORTH];
  2114. if (Point1.Y < Point2.Y) then
  2115. Result := Result + [SOUTH];
  2116. end;
  2117. { Get image rectangle }
  2118. function TBGRAImageManipulation.getImageRect(Picture: TBGRABitmap): TRect;
  2119. var
  2120. calcWidth, calcHeight, finalWidth, finalHeight, imageWidth, imageHeight: integer;
  2121. WorkRect: TRect;
  2122. begin
  2123. // Determine picture size
  2124. imageWidth := Picture.Width;
  2125. imageHeight := Picture.Height;
  2126. // Determine Work rectangle to final size
  2127. WorkRect := getWorkRect;
  2128. finalWidth := WorkRect.Right - WorkRect.Left;
  2129. finalHeight := WorkRect.Bottom - WorkRect.Top;
  2130. // Recalculate image dimensions
  2131. calcHeight := (finalWidth * imageHeight) div imageWidth;
  2132. calcWidth := finalWidth;
  2133. if (calcHeight > finalHeight) then
  2134. begin
  2135. calcHeight := finalHeight;
  2136. calcWidth := (calcHeight * imageWidth) div imageHeight;
  2137. end;
  2138. with Result do
  2139. begin
  2140. Left := 0;
  2141. Top := 0;
  2142. Right := calcWidth;
  2143. Bottom := calcHeight;
  2144. end;
  2145. end;
  2146. { Get work area rectangle }
  2147. function TBGRAImageManipulation.getWorkRect: TRect;
  2148. begin
  2149. // Get the coordinates of the control
  2150. if (fVirtualScreen <> nil) then
  2151. Result := Rect(0, 0, fVirtualScreen.Width, fVirtualScreen.Height)
  2152. else
  2153. Result := GetClientRect;
  2154. // Remove the non-work areas from our work rectangle
  2155. InflateRect(Result, -fBorderSize, -fBorderSize);
  2156. end;
  2157. { Check if mouse is over any anchor }
  2158. function TBGRAImageManipulation.isOverAnchor(APoint :TPoint; var AnchorSelected :TDirection; var ACursor :TCursor):TCropArea;
  2159. var
  2160. i :Integer;
  2161. function _isOverAnchor(APoint: TPoint; Corner: TPoint): boolean;
  2162. begin
  2163. Result := ((APoint.X >= (Corner.X - AnchorSize)) and
  2164. (APoint.X <= (Corner.X + AnchorSize)) and
  2165. (APoint.Y >= (Corner.Y - AnchorSize)) and
  2166. (APoint.Y <= (Corner.Y + AnchorSize)));
  2167. end;
  2168. function TestArea(rCropArea :TCropArea):TCropArea;
  2169. var
  2170. rCropRect,
  2171. rCropRectI :TRect;
  2172. begin
  2173. Result :=nil;
  2174. rCropRectI :=rCropArea.ScaledArea;
  2175. InflateRect(rCropRectI, AnchorSize, AnchorSize);
  2176. if ({$IFNDEF FPC}BGRAGraphics.{$ENDIF}PtInRect(rCropRectI, APoint)) then
  2177. begin
  2178. rCropRect :=rCropArea.ScaledArea;
  2179. // Verifies that is positioned on an anchor
  2180. // NW
  2181. if (_isOverAnchor(APoint, rCropRect.TopLeft)) then
  2182. begin
  2183. AnchorSelected := [NORTH, WEST];
  2184. ACursor := crSizeNW;
  2185. Result :=rCropArea; exit;
  2186. end;
  2187. // W
  2188. if (_isOverAnchor(APoint, Point(rCropRect.Left, rCropRect.Top +
  2189. (rCropRect.Bottom - rCropRect.Top) div 2))) then
  2190. begin
  2191. AnchorSelected := [WEST];
  2192. ACursor := crSizeWE;
  2193. Result :=rCropArea; exit;
  2194. end;
  2195. // SW
  2196. if (_isOverAnchor(APoint, Point(rCropRect.Left, rCropRect.Bottom))) then
  2197. begin
  2198. AnchorSelected := [SOUTH, WEST];
  2199. ACursor := crSizeSW;
  2200. Result :=rCropArea; exit;
  2201. end;
  2202. // S
  2203. if (_isOverAnchor(APoint, Point(rCropRect.Left +
  2204. ((rCropRect.Right - rCropRect.Left) div 2), rCropRect.Bottom))) then
  2205. begin
  2206. AnchorSelected := [SOUTH];
  2207. ACursor := crSizeNS;
  2208. Result :=rCropArea; exit;
  2209. end;
  2210. // SE
  2211. if (_isOverAnchor(APoint, rCropRect.BottomRight)) then
  2212. begin
  2213. AnchorSelected := [SOUTH, EAST];
  2214. ACursor := crSizeSE;
  2215. Result :=rCropArea; exit;
  2216. end;
  2217. // E
  2218. if (_isOverAnchor(APoint, Point(rCropRect.Right, rCropRect.Top +
  2219. ((rCropRect.Bottom - rCropRect.Top) div 2)))) then
  2220. begin
  2221. AnchorSelected := [EAST];
  2222. ACursor := crSizeWE;
  2223. Result :=rCropArea; exit;
  2224. end;
  2225. // NE
  2226. if (_isOverAnchor(APoint, Point(rCropRect.Right, rCropRect.Top))) then
  2227. begin
  2228. AnchorSelected := [NORTH, EAST];
  2229. ACursor := crSizeNE;
  2230. Result :=rCropArea; exit;
  2231. end;
  2232. // N
  2233. if (_isOverAnchor(APoint, Point(rCropRect.Left +
  2234. ((rCropRect.Right - rCropRect.Left) div 2), rCropRect.Top))) then
  2235. begin
  2236. AnchorSelected := [NORTH];
  2237. ACursor := crSizeNS;
  2238. Result :=rCropArea; exit;
  2239. end;
  2240. // Verifies that is positioned on a cropping area
  2241. if (AnchorSelected = []) then
  2242. begin
  2243. if ((APoint.X >= rCropRect.Left) and (APoint.X <= rCropRect.Right) and
  2244. (APoint.Y >= rCropRect.Top) and (APoint.Y <= rCropRect.Bottom)) then
  2245. begin
  2246. AnchorSelected := [NORTH, SOUTH, EAST, WEST];
  2247. ACursor := crSizeAll;
  2248. Result :=rCropArea; exit;
  2249. end;
  2250. end;
  2251. end;
  2252. end;
  2253. begin
  2254. AnchorSelected :=[];
  2255. ACursor :=crDefault;
  2256. Result :=Nil;
  2257. if (SelectedCropArea=nil)
  2258. then for i:=rCropAreas.Count-1 downto 0 do //downto so respect ZOrder
  2259. begin
  2260. Result :=TestArea(rCropAreas[i]);
  2261. if (Result<>nil) then break;
  2262. end
  2263. else begin
  2264. //Gives precedence to the selected area
  2265. Result :=TestArea(SelectedCropArea);
  2266. if (Result=nil) then
  2267. for i:=rCropAreas.Count-1 downto 0 do
  2268. begin
  2269. if (rCropAreas[i]<>SelectedCropArea) then
  2270. begin
  2271. Result :=TestArea(rCropAreas[i]);
  2272. if (Result<>nil) then break;
  2273. end;
  2274. end;
  2275. end;
  2276. end;
  2277. procedure TBGRAImageManipulation.CreateEmptyImage;
  2278. begin
  2279. fImageBitmap.Free;
  2280. fImageBitmap :=TBGRABitmap.Create(EmptyImage.Width, EmptyImage.Height);
  2281. fImageBitmap.ResolutionUnit :=ruPixelsPerInch;
  2282. fImageBitmap.ResolutionX :=Self.PixelsPerInch;
  2283. fImageBitmap.ResolutionY :=fImageBitmap.ResolutionX;
  2284. end;
  2285. procedure TBGRAImageManipulation.CreateResampledBitmap;
  2286. var
  2287. DestinationRect: TRect;
  2288. ResampledBitmap: TBGRACustomBitmap;
  2289. begin
  2290. // Get the resampled dimensions to scale image for draw in component
  2291. DestinationRect := getImageRect(fImageBitmap);
  2292. // Recreate resampled bitmap
  2293. try
  2294. fResampledBitmap.Free;
  2295. fResampledBitmap := TBGRABitmap.Create(DestinationRect.Right - DestinationRect.Left,
  2296. DestinationRect.Bottom - DestinationRect.Top);
  2297. ResampledBitmap := fImageBitmap.Resample(DestinationRect.Right - DestinationRect.Left,
  2298. DestinationRect.Bottom - DestinationRect.Top, rmFineResample);
  2299. fResampledBitmap.BlendImage(0, 0, ResampledBitmap, boLinearBlend);
  2300. finally
  2301. ResampledBitmap.Free;
  2302. end;
  2303. end;
  2304. procedure TBGRAImageManipulation.Loaded;
  2305. begin
  2306. inherited Loaded;
  2307. if Self.Empty then
  2308. begin
  2309. CreateEmptyImage;
  2310. CreateResampledBitmap;
  2311. end;
  2312. rLoading:=False;
  2313. // Force Render Struct
  2314. RepaintBackground;
  2315. Render;
  2316. end;
  2317. { ============================================================================ }
  2318. { =====[ Component Definition ]=============================================== }
  2319. { ============================================================================ }
  2320. constructor TBGRAImageManipulation.Create(AOwner: TComponent);
  2321. var
  2322. fGCD :integer;
  2323. begin
  2324. inherited Create(AOwner);
  2325. //MaxM: csLoading in ComponentState does not work?
  2326. rLoading :=True;
  2327. // Set default component values
  2328. inherited Width := 320;
  2329. inherited Height := 240;
  2330. // Default property values
  2331. fAnchorSize := 5;
  2332. fAnchorSelected := [];
  2333. fBorderSize := 2;
  2334. fAspectRatio := '3:4';
  2335. fAspectX := 3;
  2336. fAspectY := 4;
  2337. fKeepAspectRatio := True;
  2338. // Default control values
  2339. ControlStyle := ControlStyle + [csReplicatable];
  2340. Cursor := crDefault;
  2341. // Calculate the ratio
  2342. fGCD := getGCD(fAspectX, fAspectY);
  2343. // Determine the ratio of scale per axle
  2344. with fRatio do
  2345. begin
  2346. Horizontal := fAspectX div fGCD;
  2347. Vertical := fAspectY div fGCD;
  2348. end;
  2349. // Find size limits
  2350. findSizeLimits;
  2351. // Create the Image Bitmap
  2352. fImageBitmap := TBGRABitmap.Create;
  2353. // Create the Resampled Bitmap
  2354. fResampledBitmap := TBGRABitmap.Create;
  2355. // Create the Background
  2356. fBackground := TBGRABitmap.Create(Width, Height);
  2357. // Create render surface
  2358. fVirtualScreen := TBGRABitmap.Create(Width, Height);
  2359. rEmptyImage :=TBGRAEmptyImage.Create(Self);
  2360. rNewCropAreaDefault :=TBGRANewCropAreaDefault.Create(Self);
  2361. // Initialize crop area
  2362. rCropAreas :=TCropAreaList.Create(Self);
  2363. rCropAreas.Name:='CropAreas';
  2364. rNewCropArea :=Nil;
  2365. rSelectedCropArea :=Nil;
  2366. fMouseCaught := False;
  2367. end;
  2368. destructor TBGRAImageManipulation.Destroy;
  2369. begin
  2370. fImageBitmap.Free;
  2371. fResampledBitmap.Free;
  2372. fBackground.Free;
  2373. fVirtualScreen.Free;
  2374. rEmptyImage.Free;
  2375. rNewCropAreaDefault.Free;
  2376. rCropAreas.Free;
  2377. inherited Destroy;
  2378. end;
  2379. procedure TBGRAImageManipulation.Invalidate;
  2380. begin
  2381. inherited Invalidate;
  2382. end;
  2383. procedure TBGRAImageManipulation.Paint;
  2384. begin
  2385. inherited Paint;
  2386. fVirtualScreen.Draw(Canvas, 0, 0, True);
  2387. end;
  2388. { This function repaint the background only when necessary to avoid unnecessary
  2389. redraws. Contain a function called DrawCheckers that draws the Background like
  2390. checkers game. Also included was a function that draws 3D effects changed to
  2391. allow color changes. }
  2392. procedure TBGRAImageManipulation.RepaintBackground;
  2393. procedure DrawCheckers(bmp: TBGRABitmap; ARect: TRect);
  2394. const
  2395. tx = 8;
  2396. ty = 8;
  2397. var
  2398. xb, yb, xdest, ydest, nbx, nby: integer;
  2399. oddColor, evenColor: TBGRAPixel;
  2400. begin
  2401. oddColor := BGRA(220, 220, 220);
  2402. evenColor := BGRA(255, 255, 255);
  2403. bmp.ClipRect := ARect;
  2404. xdest := ARect.Left;
  2405. nbx := ((ARect.Right - ARect.Left) + tx - 1) div tx;
  2406. nby := ((ARect.Bottom - ARect.Top) + ty - 1) div ty;
  2407. for xb := 0 to nbx - 1 do
  2408. begin
  2409. ydest := ARect.Top;
  2410. for yb := 0 to nby - 1 do
  2411. begin
  2412. if odd(xb + yb) then
  2413. bmp.FillRect(xdest, ydest, xdest + tx, ydest + ty, oddColor, dmSet)
  2414. else
  2415. bmp.FillRect(xdest, ydest, xdest + tx, ydest + ty, evenColor, dmSet);
  2416. Inc(ydest, ty);
  2417. end;
  2418. Inc(xdest, tx);
  2419. end;
  2420. bmp.NoClip;
  2421. end;
  2422. var
  2423. Border: TRect;
  2424. Grad: TBGRAGradientScanner;
  2425. begin
  2426. // Resize background
  2427. fBackground.SetSize(fVirtualScreen.Width, fVirtualScreen.Height);
  2428. // Draw the outer bevel
  2429. Border := Rect(0, 0, fVirtualScreen.Width, fVirtualScreen.Height);
  2430. // Draw the rectangle around image
  2431. if (fBorderSize > 2) then
  2432. begin
  2433. // Draw the border gradient
  2434. Grad := TBGRAGradientScanner.Create(BGRA(245, 245, 245),
  2435. BGRA(205, 204, 203), gtLinear, PointF(0, 0), PointF(0, fBackground.Height));
  2436. fBackground.FillRect(0, 0, fBackground.Width, fBorderSize - 2, Grad, dmSet);
  2437. fBackground.FillRect(0, fBorderSize - 2, fBorderSize - 2,
  2438. fBackground.Height - fBorderSize + 2, Grad, dmSet);
  2439. fBackground.FillRect(fBackground.Width - fBorderSize + 2, fBorderSize - 2,
  2440. fBackground.Width, fBackground.Height - fBorderSize + 2,
  2441. Grad, dmSet);
  2442. fBackground.FillRect(0, fBackground.Height - fBorderSize + 2,
  2443. fBackground.Width, fBackground.Height, Grad, dmSet);
  2444. Grad.Free;
  2445. InflateRect(Border, -(fBorderSize - 2), -(fBorderSize - 2));
  2446. end;
  2447. // Draw 3D border
  2448. fBackground.CanvasBGRA.Frame3D(Border, 1, bvLowered,
  2449. clBtnHighlight, cl3DDkShadow);
  2450. fBackground.CanvasBGRA.Frame3D(Border, 1, bvLowered,
  2451. cl3DLight, clBtnShadow);
  2452. DrawCheckers(fBackground, Border);
  2453. end;
  2454. { Resize the component, recalculating the proportions }
  2455. procedure TBGRAImageManipulation.Resize;
  2456. function min(const Value: integer; const MinValue: integer): integer;
  2457. begin
  2458. if (Value < MinValue) then
  2459. Result := MinValue
  2460. else
  2461. Result := Value;
  2462. end;
  2463. var
  2464. i :Integer;
  2465. curCropArea :TCropArea;
  2466. begin
  2467. inherited Resize;
  2468. //MaxM: Maybe csLoading in ComponentState but it does not work
  2469. //if rLoading then exit;
  2470. if (fVirtualScreen <> nil) then
  2471. begin
  2472. fVirtualScreen.SetSize(min(Self.Width, (fBorderSize * 2 + fAnchorSize + fMinWidth)),
  2473. min(Self.Height, (fBorderSize * 2 + fAnchorSize + fMinHeight)));
  2474. fVirtualScreen.InvalidateBitmap;
  2475. if Self.Empty
  2476. then CreateEmptyImage;
  2477. CreateResampledBitmap;
  2478. for i:=0 to rCropAreas.Count-1 do
  2479. begin
  2480. curCropArea :=rCropAreas[i];
  2481. curCropArea.CalculateScaledAreaFromArea;
  2482. if curCropArea.isNullSize then
  2483. begin
  2484. // A Null-size crop selection (delete it or assign max size?)
  2485. //CalcMaxSelection(curCropArea);
  2486. end;
  2487. end;
  2488. // Force Render Struct
  2489. RepaintBackground;
  2490. Render;
  2491. end;
  2492. Invalidate;
  2493. end;
  2494. { Function responsible for rendering the content of the component, including
  2495. the selection border and anchors. The selected area is painted with a
  2496. different transparency level for easy viewing of what will be cut. }
  2497. procedure TBGRAImageManipulation.Render;
  2498. var
  2499. WorkRect, emptyRect: TRect;
  2500. Mask: TBGRABitmap;
  2501. BorderColor, SelectColor,
  2502. FillColor, IcoColor: TBGRAPixel;
  2503. curCropArea :TCropArea;
  2504. curCropAreaRect :TRect;
  2505. i: Integer;
  2506. TextS:TTextStyle;
  2507. begin
  2508. // This procedure render main feature of engine
  2509. // Render background
  2510. fVirtualScreen.BlendImage(0, 0, fBackground, boLinearBlend);
  2511. // Render the image
  2512. // Find the working area of the component
  2513. WorkRect := getWorkRect;
  2514. try
  2515. // Draw image
  2516. fVirtualScreen.BlendImage(WorkRect.Left, WorkRect.Top, fResampledBitmap, boLinearBlend);
  2517. // Render the selection background area
  2518. BorderColor := BGRAWhite;
  2519. FillColor := BGRA(0, 0, 0, 128);
  2520. Mask := TBGRABitmap.Create(WorkRect.Right - WorkRect.Left, WorkRect.Bottom - WorkRect.Top, FillColor);
  2521. if Self.Empty and rEmptyImage.ShowBorder then
  2522. begin
  2523. emptyRect :=Rect(0,0,fResampledBitmap.Width-1, fResampledBitmap.Height-1);
  2524. Mask.CanvasBGRA.Frame3d(emptyRect, 1, bvRaised, BGRA(255, 255, 255, 180), BGRA(0, 0, 0, 160));
  2525. //Mask.Rectangle(emptyRect, BorderColor, BGRAPixelTransparent); //wich one?
  2526. end;
  2527. for i:=0 to rCropAreas.Count-1 do
  2528. begin
  2529. curCropArea :=rCropAreas[i];
  2530. curCropAreaRect :=curCropArea.ScaledArea;
  2531. //Colors
  2532. SelectColor := BGRA(255, 255, 0, 255);
  2533. FillColor := BGRA(255, 255, 0, 128);
  2534. if (curCropArea = SelectedCropArea)
  2535. then begin
  2536. BorderColor := BGRA(255, 0, 0, 255);
  2537. IcoColor :=BorderColor;
  2538. end
  2539. else begin
  2540. if (curCropArea = rNewCropArea)
  2541. then BorderColor := BGRA(255, 0, 255, 255)
  2542. else BorderColor := curCropArea.BorderColor;
  2543. IcoColor :=SelectColor;
  2544. end;
  2545. Mask.EraseRectAntialias(curCropAreaRect.Left, curCropAreaRect.Top, curCropAreaRect.Right-1,
  2546. curCropAreaRect.Bottom-1, 255);
  2547. // Draw a selection box
  2548. with Rect(curCropAreaRect.Left, curCropAreaRect.Top, curCropAreaRect.Right-1, curCropAreaRect.Bottom-1) do
  2549. Mask.DrawPolyLineAntialias([Point(Left, Top), Point(Right, Top), Point(Right, Bottom), Point(Left, Bottom), Point(Left, Top)],
  2550. BorderColor, BGRAPixelTransparent, 1, False);
  2551. //Draw Icons
  2552. { #todo 1 -oMaxM : Draw Other Icons }
  2553. if (cIcoIndex in curCropArea.Icons) then
  2554. begin
  2555. TextS.Alignment:=taCenter;
  2556. TextS.SystemFont:=True;
  2557. TextS.Layout:=tlCenter;
  2558. TextS.SingleLine:=True;
  2559. Mask.FontHeight:=12;
  2560. Mask.FontStyle:=[fsBold];
  2561. Mask.EllipseAntialias(curCropAreaRect.Right-12, curCropAreaRect.Top+12, 4,4, IcoColor, 8);
  2562. Mask.TextRect(Rect(curCropAreaRect.Right-18, curCropAreaRect.Top+2, curCropAreaRect.Right-4, curCropAreaRect.Top+24),
  2563. curCropAreaRect.Right-12, curCropAreaRect.Top+12,
  2564. IntToStr(curCropArea.getIndex), TextS, BGRAWhite);
  2565. end;
  2566. // Draw anchors
  2567. BorderColor := BGRABlack;
  2568. // NW
  2569. Mask.Rectangle(curCropAreaRect.Left-fAnchorSize, curCropAreaRect.Top-fAnchorSize,
  2570. curCropAreaRect.Left+fAnchorSize+1, curCropAreaRect.Top+fAnchorSize+1,
  2571. BorderColor, FillColor, dmSet);
  2572. // W
  2573. Mask.Rectangle(curCropAreaRect.Left-fAnchorSize,
  2574. (curCropAreaRect.Top+((curCropAreaRect.Bottom - curCropAreaRect.Top) div 2))-fAnchorSize,
  2575. curCropAreaRect.Left+fAnchorSize+1,
  2576. (curCropAreaRect.Top+((curCropAreaRect.Bottom - curCropAreaRect.Top) div 2))+fAnchorSize+1,
  2577. BorderColor, FillColor, dmSet);
  2578. // SW
  2579. Mask.Rectangle(curCropAreaRect.Left-fAnchorSize, curCropAreaRect.Bottom-fAnchorSize-1,
  2580. curCropAreaRect.Left+fAnchorSize+1, curCropAreaRect.Bottom+fAnchorSize,
  2581. BorderColor, FillColor, dmSet);
  2582. // S
  2583. if ((fAnchorSelected = [NORTH]) and (curCropAreaRect.Top < curCropAreaRect.Bottom) and
  2584. (fStartPoint.Y = curCropAreaRect.Top)) or ((fAnchorSelected = [NORTH]) and
  2585. (curCropAreaRect.Top > curCropAreaRect.Bottom) and (fStartPoint.Y = curCropAreaRect.Top)) or
  2586. ((fAnchorSelected = [SOUTH]) and (curCropAreaRect.Top < curCropAreaRect.Bottom) and
  2587. (fStartPoint.Y = curCropAreaRect.Top)) or ((fAnchorSelected = [SOUTH]) and
  2588. (curCropAreaRect.Top > curCropAreaRect.Bottom) and (fStartPoint.Y = curCropAreaRect.Top))
  2589. then Mask.Rectangle((curCropAreaRect.Left+((curCropAreaRect.Right-curCropAreaRect.Left) div 2))-fAnchorSize,
  2590. curCropAreaRect.Bottom-fAnchorSize-1, (curCropAreaRect.Left+((curCropAreaRect.Right - curCropAreaRect.Left) div 2))+fAnchorSize+1,
  2591. curCropAreaRect.Bottom+fAnchorSize,
  2592. BorderColor, SelectColor, dmSet)
  2593. else Mask.Rectangle((curCropAreaRect.Left+((curCropAreaRect.Right-curCropAreaRect.Left) div 2))-fAnchorSize,
  2594. curCropAreaRect.Bottom-fAnchorSize-1, (curCropAreaRect.Left+((curCropAreaRect.Right-curCropAreaRect.Left) div 2))+fAnchorSize+1,
  2595. curCropAreaRect.Bottom+fAnchorSize,
  2596. BorderColor, FillColor, dmSet);
  2597. // SE
  2598. if ((fAnchorSelected = [NORTH, WEST]) and
  2599. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2600. ((fAnchorSelected = [NORTH, WEST]) and
  2601. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom))) or
  2602. ((fAnchorSelected = [NORTH, WEST]) and
  2603. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom))) or
  2604. ((fAnchorSelected = [NORTH, WEST]) and
  2605. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2606. ((fAnchorSelected = [NORTH, EAST]) and
  2607. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2608. ((fAnchorSelected = [NORTH, EAST]) and
  2609. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom))) or
  2610. ((fAnchorSelected = [NORTH, EAST]) and
  2611. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom))) or
  2612. ((fAnchorSelected = [NORTH, EAST]) and
  2613. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2614. ((fAnchorSelected = [SOUTH, EAST]) and
  2615. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2616. ((fAnchorSelected = [SOUTH, EAST]) and
  2617. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom))) or
  2618. ((fAnchorSelected = [SOUTH, EAST]) and
  2619. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom))) or
  2620. ((fAnchorSelected = [SOUTH, EAST]) and
  2621. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2622. ((fAnchorSelected = [SOUTH, WEST]) and
  2623. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom))) or
  2624. ((fAnchorSelected = [SOUTH, WEST]) and
  2625. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2626. ((fAnchorSelected = [SOUTH, WEST]) and
  2627. ((curCropAreaRect.Left > curCropAreaRect.Right) and (curCropAreaRect.Top > curCropAreaRect.Bottom))) or
  2628. ((fAnchorSelected = [SOUTH, WEST]) and
  2629. ((curCropAreaRect.Left < curCropAreaRect.Right) and (curCropAreaRect.Top < curCropAreaRect.Bottom)))
  2630. then Mask.Rectangle(curCropAreaRect.Right-fAnchorSize-1,
  2631. curCropAreaRect.Bottom-fAnchorSize-1, curCropAreaRect.Right+fAnchorSize, curCropAreaRect.Bottom+fAnchorSize,
  2632. BorderColor, SelectColor, dmSet)
  2633. else Mask.Rectangle(curCropAreaRect.Right-fAnchorSize-1,
  2634. curCropAreaRect.Bottom-fAnchorSize-1, curCropAreaRect.Right+fAnchorSize, curCropAreaRect.Bottom+fAnchorSize,
  2635. BorderColor, FillColor, dmSet);
  2636. // E
  2637. if ((fAnchorSelected = [EAST]) and (curCropAreaRect.Left < curCropAreaRect.Right) and
  2638. (fStartPoint.X = curCropAreaRect.Left)) or ((fAnchorSelected = [EAST]) and
  2639. (curCropAreaRect.Left > curCropAreaRect.Right) and (fStartPoint.X = curCropAreaRect.Left)) or
  2640. ((fAnchorSelected = [WEST]) and (curCropAreaRect.Left < curCropAreaRect.Right) and
  2641. (fStartPoint.X = curCropAreaRect.Left)) or ((fAnchorSelected = [WEST]) and
  2642. (curCropAreaRect.Left > curCropAreaRect.Right) and (fStartPoint.X = curCropAreaRect.Left))
  2643. then Mask.Rectangle(curCropAreaRect.Right-fAnchorSize-1,
  2644. (curCropAreaRect.Top+((curCropAreaRect.Bottom - curCropAreaRect.Top) div 2))-fAnchorSize,
  2645. curCropAreaRect.Right+fAnchorSize, (curCropAreaRect.Top+((curCropAreaRect.Bottom-curCropAreaRect.Top) div 2))+fAnchorSize+1,
  2646. BorderColor, SelectColor, dmSet)
  2647. else Mask.Rectangle(curCropAreaRect.Right-fAnchorSize-1, (curCropAreaRect.Top+((curCropAreaRect.Bottom-curCropAreaRect.Top) div 2))-fAnchorSize,
  2648. curCropAreaRect.Right+fAnchorSize, (curCropAreaRect.Top+((curCropAreaRect.Bottom-curCropAreaRect.Top) div 2))+fAnchorSize+1,
  2649. BorderColor, FillColor, dmSet);
  2650. // NE
  2651. Mask.Rectangle(curCropAreaRect.Right-fAnchorSize-1, curCropAreaRect.Top-fAnchorSize,
  2652. curCropAreaRect.Right+fAnchorSize, curCropAreaRect.Top+fAnchorSize+1,
  2653. BorderColor, FillColor, dmSet);
  2654. // N
  2655. Mask.Rectangle((curCropAreaRect.Left+((curCropAreaRect.Right-curCropAreaRect.Left) div 2))-fAnchorSize,
  2656. curCropAreaRect.Top-fAnchorSize, (curCropAreaRect.Left+((curCropAreaRect.Right-curCropAreaRect.Left) div 2))+fAnchorSize+1,
  2657. curCropAreaRect.Top+fAnchorSize+1,
  2658. BorderColor, FillColor, dmSet);
  2659. end;
  2660. finally
  2661. fVirtualScreen.BlendImage(WorkRect.Left, WorkRect.Top, Mask, boLinearBlend);
  2662. Mask.Free;
  2663. end;
  2664. end;
  2665. { ============================================================================ }
  2666. { =====[ Properties Manipulation ]============================================ }
  2667. { ============================================================================ }
  2668. function TBGRAImageManipulation.getAnchorSize: byte;
  2669. begin
  2670. Result := fAnchorSize * 2 + 1;
  2671. end;
  2672. function TBGRAImageManipulation.getPixelsPerInch: Integer;
  2673. begin
  2674. if (Owner is TCustomForm)
  2675. then Result :=TCustomForm(Owner).PixelsPerInch
  2676. else Result :=96;
  2677. end;
  2678. procedure TBGRAImageManipulation.setAnchorSize(const Value: byte);
  2679. const
  2680. MinSize = 3;
  2681. MaxSize = 9;
  2682. begin
  2683. if (Value <> getAnchorSize) then
  2684. begin
  2685. if (Value < MinSize) then
  2686. begin
  2687. raise ERangeError.CreateFmt(SAnchorSizeIsTooSmall,
  2688. [Value, MinSize, MaxSize]);
  2689. end
  2690. else
  2691. begin
  2692. if (Value > MaxSize) then
  2693. begin
  2694. raise ERangeError.CreateFmt(SAnchorSizeIsTooLarge,
  2695. [Value, MinSize, MaxSize]);
  2696. end
  2697. else
  2698. begin
  2699. if ((Value mod 2) = 0) then
  2700. begin
  2701. raise EInvalidArgument.CreateFmt(SAnchorSizeIsNotOdd, [Value]);
  2702. end
  2703. else
  2704. begin
  2705. fAnchorSize := (Value div 2);
  2706. Render;
  2707. Refresh;
  2708. end;
  2709. end;
  2710. end;
  2711. end;
  2712. end;
  2713. function TBGRAImageManipulation.getEmpty: boolean;
  2714. begin
  2715. Result := fImageBitmap.Empty or (fImageBitmap.Width = 0) or (fImageBitmap.Height = 0);
  2716. end;
  2717. function TBGRAImageManipulation.getResampledBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False): TBGRABitmap;
  2718. begin
  2719. Result := fImageBitmap;
  2720. if not (fImageBitmap.Empty) then
  2721. begin
  2722. if (ACropArea = Nil)
  2723. then ACropArea := Self.SelectedCropArea;
  2724. if (ACropArea <> Nil)
  2725. then Result :=ACropArea.getResampledBitmap(ACopyProperties);
  2726. end;
  2727. end;
  2728. function TBGRAImageManipulation.getBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False): TBGRABitmap;
  2729. begin
  2730. Result := fImageBitmap;
  2731. if not (fImageBitmap.Empty) then
  2732. begin
  2733. if (ACropArea = Nil)
  2734. then ACropArea := Self.SelectedCropArea;
  2735. if (ACropArea <> Nil)
  2736. then Result :=ACropArea.getBitmap(ACopyProperties);
  2737. end;
  2738. end;
  2739. procedure TBGRAImageManipulation.setBitmap(const Value: TBGRABitmap);
  2740. var
  2741. curCropArea: TCropArea;
  2742. i: Integer;
  2743. begin
  2744. if (Value <> fImageBitmap) then
  2745. begin
  2746. try
  2747. if Value.Empty or (Value.Width = 0) or (Value.Height = 0)
  2748. then CreateEmptyImage
  2749. else begin
  2750. // Clear actual image
  2751. fImageBitmap.Free;
  2752. fImageBitmap :=TBGRABitmap.Create(Value.Width, Value.Height);
  2753. fImageBitmap.Assign(Value, True); // Associate the new bitmap
  2754. end;
  2755. CreateResampledBitmap;
  2756. for i:=0 to rCropAreas.Count-1 do
  2757. begin
  2758. curCropArea :=rCropAreas[i];
  2759. curCropArea.CalculateScaledAreaFromArea;
  2760. if curCropArea.isNullSize then
  2761. begin
  2762. // A Null-size crop selection (delete it or assign max size?)
  2763. //CalcMaxSelection(curCropArea);
  2764. end;
  2765. end;
  2766. finally
  2767. // Force Render Struct
  2768. Render;
  2769. inherited Invalidate;
  2770. end;
  2771. end;
  2772. end;
  2773. procedure TBGRAImageManipulation.rotateLeft(ACopyProperties: Boolean=False);
  2774. var
  2775. TempBitmap: TBGRACustomBitmap;
  2776. curCropArea :TCropArea;
  2777. i :Integer;
  2778. begin
  2779. try
  2780. // Prevent empty image
  2781. if Self.Empty then exit;
  2782. // Rotate bitmap
  2783. TempBitmap := fImageBitmap.RotateCCW(ACopyProperties);
  2784. fImageBitmap.Assign(TempBitmap);
  2785. CreateResampledBitmap;
  2786. { #todo -oMaxM : Rotate the Crop Areas? a bool published property? }
  2787. for i:=0 to rCropAreas.Count-1 do
  2788. begin
  2789. curCropArea :=rCropAreas[i];
  2790. curCropArea.CalculateScaledAreaFromArea;
  2791. if curCropArea.isNullSize then
  2792. begin
  2793. // A Null-size crop selection (delete it or assign max size?)
  2794. //CalcMaxSelection(curCropArea);
  2795. end;
  2796. end;
  2797. finally
  2798. // Force Render Struct
  2799. Render;
  2800. inherited Invalidate;
  2801. TempBitmap.Free;
  2802. end;
  2803. end;
  2804. procedure TBGRAImageManipulation.rotateRight(ACopyProperties: Boolean=False);
  2805. var
  2806. TempBitmap: TBGRACustomBitmap;
  2807. curCropArea :TCropArea;
  2808. i :Integer;
  2809. begin
  2810. try
  2811. // Prevent empty image
  2812. if Self.Empty then exit;
  2813. // Rotate bitmap
  2814. TempBitmap := fImageBitmap.RotateCW(ACopyProperties);
  2815. fImageBitmap.Assign(TempBitmap);
  2816. CreateResampledBitmap;
  2817. { #todo -oMaxM : Rotate the Crop Areas? a bool published property? }
  2818. for i:=0 to rCropAreas.Count-1 do
  2819. begin
  2820. curCropArea :=rCropAreas[i];
  2821. curCropArea.CalculateScaledAreaFromArea;
  2822. if curCropArea.isNullSize then
  2823. begin
  2824. // A Null-size crop selection (delete it or assign max size?)
  2825. //CalcMaxSelection(curCropArea);
  2826. end;
  2827. end;
  2828. finally
  2829. // Force Render Struct
  2830. Render;
  2831. inherited Invalidate;
  2832. TempBitmap.Free;
  2833. end;
  2834. end;
  2835. procedure TBGRAImageManipulation.tests;
  2836. begin
  2837. // Self.AutoSize:=False;
  2838. // Render;
  2839. // Refresh;
  2840. end;
  2841. function TBGRAImageManipulation.addCropArea(AArea: TRectF; AAreaUnit: TResolutionUnit;
  2842. AUserData: Integer): TCropArea;
  2843. var
  2844. newCropArea :TCropArea;
  2845. begin
  2846. try
  2847. newCropArea :=TCropArea.Create(Self, AArea, AAreaUnit, AUserData);
  2848. newCropArea.BorderColor:= BGRAWhite;
  2849. rNewCropAreaDefault.CopyPropertiesToArea(newCropArea);
  2850. rCropAreas.add(newCropArea);
  2851. if (rSelectedCropArea = nil)
  2852. then rSelectedCropArea :=newCropArea;
  2853. newCropArea.CalculateScaledAreaFromArea;
  2854. Result :=newCropArea;
  2855. except
  2856. if (newCropArea <> Nil)
  2857. then newCropArea.Free;
  2858. Result :=Nil;
  2859. end;
  2860. Render;
  2861. Invalidate;
  2862. end;
  2863. function TBGRAImageManipulation.addScaledCropArea(AArea: TRect; AUserData: Integer): TCropArea;
  2864. begin
  2865. Result :=Self.addCropArea(RectF(0,0,0,0), rNewCropAreaDefault.rResolutionUnit, AUserData);
  2866. Result.ScaledArea :=AArea;
  2867. if (fMouseCaught)
  2868. then Result.CalculateAreaFromScaledArea;
  2869. Render;
  2870. Invalidate;
  2871. end;
  2872. procedure TBGRAImageManipulation.delCropArea(ACropArea: TCropArea);
  2873. var
  2874. curIndex, newIndex :Integer;
  2875. begin
  2876. if (ACropArea <> Nil) then
  2877. begin
  2878. curIndex :=rCropAreas.IndexOf(ACropArea);
  2879. //determines the new SelectedCropArea
  2880. if (ACropArea = SelectedCropArea) then
  2881. begin
  2882. if (rCropAreas.Count = 1)
  2883. then SelectedCropArea :=nil
  2884. else begin
  2885. newIndex :=curIndex-1;
  2886. if (newIndex < 0)
  2887. then newIndex :=rCropAreas.Count-1;
  2888. SelectedCropArea :=rCropAreas.items[newIndex];
  2889. end;
  2890. end;
  2891. rCropAreas.Delete(curIndex);
  2892. Render;
  2893. Invalidate;
  2894. end;
  2895. end;
  2896. procedure TBGRAImageManipulation.clearCropAreas;
  2897. begin
  2898. rCropAreas.Clear;
  2899. Render;
  2900. Invalidate;
  2901. end;
  2902. procedure TBGRAImageManipulation.getAllResampledBitmaps(ACallBack: TgetAllBitmapsCallback; AUserData:Integer; ACopyProperties: Boolean=False);
  2903. var
  2904. i :Integer;
  2905. curBitmap :TBGRABitmap;
  2906. begin
  2907. //Get Resampled Bitmap of each CropArea and pass it to CallBack
  2908. for i:=0 to rCropAreas.Count-1 do
  2909. try
  2910. curBitmap :=rCropAreas[i].getResampledBitmap(ACopyProperties);
  2911. ACallBack(curBitmap, rCropAreas[i], AUserData);
  2912. finally
  2913. if (curBitmap<>nil)
  2914. then curBitmap.Free;
  2915. end;
  2916. end;
  2917. procedure TBGRAImageManipulation.getAllBitmaps(ACallBack: TgetAllBitmapsCallback; AUserData:Integer; ACopyProperties: Boolean=False);
  2918. var
  2919. i :Integer;
  2920. curBitmap :TBGRABitmap;
  2921. begin
  2922. //Get Bitmap of each CropArea and pass it to CallBack
  2923. for i:=0 to rCropAreas.Count-1 do
  2924. try
  2925. curBitmap :=rCropAreas[i].getBitmap(ACopyProperties);
  2926. ACallBack(curBitmap, rCropAreas[i], AUserData);
  2927. finally
  2928. if (curBitmap<>nil)
  2929. then curBitmap.Free;
  2930. end;
  2931. end;
  2932. procedure TBGRAImageManipulation.SetEmptyImageSizeToCropAreas(ReduceLarger: Boolean);
  2933. var
  2934. i :Integer;
  2935. curCropAreaRect :TRectF;
  2936. curCropArea :TCropArea;
  2937. mWidth, mHeight:Single;
  2938. xRatio, yRatio, resX :Single;
  2939. begin
  2940. if Self.Empty and (rCropAreas.Count>0) then
  2941. begin
  2942. if ReduceLarger
  2943. then begin
  2944. mWidth:=0;
  2945. mHeight:=0;
  2946. end
  2947. else begin
  2948. mWidth:=EmptyImage.ResolutionWidth;
  2949. mHeight:=EmptyImage.ResolutionHeight;
  2950. if (mWidth=0) or (mHeight=0) then
  2951. begin
  2952. mWidth :=ResolutionUnitConvert(fImageBitmap.Width, ruNone, EmptyImage.ResolutionUnit, Self.PixelsPerInch);
  2953. mHeight :=ResolutionUnitConvert(fImageBitmap.Height, ruNone, EmptyImage.ResolutionUnit, Self.PixelsPerInch);
  2954. end;
  2955. end;
  2956. for i:=0 to rCropAreas.Count-1 do
  2957. begin
  2958. curCropArea :=rCropAreas[i];
  2959. curCropAreaRect :=curCropArea.Area;
  2960. curCropAreaRect.Right :=ResolutionUnitConvert(curCropAreaRect.Right, curCropArea.rAreaUnit,
  2961. EmptyImage.ResolutionUnit, Self.PixelsPerInch);
  2962. curCropAreaRect.Bottom :=ResolutionUnitConvert(curCropAreaRect.Bottom, curCropArea.rAreaUnit,
  2963. EmptyImage.ResolutionUnit, Self.PixelsPerInch);
  2964. if (curCropAreaRect.Right > mWidth)
  2965. then mWidth :=curCropAreaRect.Right;
  2966. if (curCropAreaRect.Bottom > mHeight)
  2967. then mHeight :=curCropAreaRect.Bottom;
  2968. end;
  2969. EmptyImage.ResolutionWidth :=mWidth;
  2970. EmptyImage.ResolutionHeight :=mHeight;
  2971. Resize;
  2972. end;
  2973. end;
  2974. procedure TBGRAImageManipulation.SetEmptyImageSizeToNull;
  2975. begin
  2976. SetEmptyImageSize(ruPixelsPerInch, 0, 0);
  2977. end;
  2978. procedure TBGRAImageManipulation.SetEmptyImageSize(AResolutionUnit: TResolutionUnit; AResolutionWidth,
  2979. AResolutionHeight: Single);
  2980. begin
  2981. EmptyImage.ResolutionUnit:=AResolutionUnit;
  2982. EmptyImage.rResolutionWidth:=AResolutionWidth;
  2983. EmptyImage.rResolutionHeight:=AResolutionHeight;
  2984. Resize;
  2985. end;
  2986. procedure TBGRAImageManipulation.setBorderSize(const Value: byte);
  2987. const
  2988. MinSize = 2;
  2989. MaxSize = 10;
  2990. begin
  2991. if (Value <> fBorderSize) then
  2992. begin
  2993. if (Value < MinSize) then
  2994. begin
  2995. raise ERangeError.CreateFmt(SBorderSizeIsTooSmall,
  2996. [Value, MinSize, MaxSize]);
  2997. end
  2998. else
  2999. begin
  3000. if (Value > MaxSize) then
  3001. begin
  3002. raise ERangeError.CreateFmt(SBorderSizeIsTooLarge,
  3003. [Value, MinSize, MaxSize]);
  3004. end
  3005. else
  3006. begin
  3007. fBorderSize := Value;
  3008. Resize;
  3009. end;
  3010. end;
  3011. end;
  3012. end;
  3013. procedure TBGRAImageManipulation.setKeepAspectRatio(const Value: boolean);
  3014. var
  3015. i :Integer;
  3016. curCropArea :TCropArea;
  3017. imgPresent :Boolean;
  3018. begin
  3019. if (Value = fKeepAspectRatio) then Exit;
  3020. fKeepAspectRatio :=Value;
  3021. imgPresent :=not(fImageBitmap.Empty);
  3022. //Change all the Crop Area with KeepAspectRatio=bParent
  3023. for i:=0 to rCropAreas.Count-1 do
  3024. begin
  3025. curCropArea :=rCropAreas[i];
  3026. if (curCropArea<>nil) and (curCropArea.KeepAspectRatio=bParent) then
  3027. begin
  3028. if fKeepAspectRatio
  3029. then curCropArea.CopyAspectFromParent;
  3030. if imgPresent
  3031. then ApplyRatioToArea(curCropArea);
  3032. end;
  3033. end;
  3034. if imgPresent
  3035. then Render;
  3036. Invalidate;
  3037. end;
  3038. function TBGRAImageManipulation.getAspectRatioFromImage(
  3039. const Value: TBGRABitmap): string;
  3040. var
  3041. GCD: integer;
  3042. begin
  3043. GCD := getGCD(Value.Width, Value.Height);
  3044. Result := IntToStr(Value.Width div GCD) + ':' + IntToStr(Value.Height div GCD);
  3045. end;
  3046. procedure TBGRAImageManipulation.setAspectRatio(const Value: string);
  3047. var
  3048. XValue, YValue: integer;
  3049. AspectRatioText: string;
  3050. i :Integer;
  3051. fGCD :integer;
  3052. imgPresent :Boolean;
  3053. curCropArea :TCropArea;
  3054. begin
  3055. if (Value <> fAspectRatio) then
  3056. begin
  3057. // Check if value contain a valid string
  3058. CheckAspectRatio(Value, AspectRatioText, XValue, YValue);
  3059. // Set new Aspect Ratio
  3060. fAspectRatio := AspectRatioText;
  3061. fAspectX := XValue;
  3062. fAspectY := YValue;
  3063. // Calculate the ratio
  3064. fGCD := getGCD(fAspectX, fAspectY);
  3065. // Determine the ratio of scale per axle
  3066. with fRatio do
  3067. begin
  3068. Horizontal := fAspectX div fGCD;
  3069. Vertical := fAspectY div fGCD;
  3070. end;
  3071. // Set minimun size
  3072. if ((fRatio.Horizontal < fAnchorSize + 10) or
  3073. (fRatio.Vertical < fAnchorSize + 10)) then
  3074. begin
  3075. fMinWidth := fRatio.Horizontal * 10;
  3076. fMinHeight := fRatio.Vertical * 10;
  3077. end
  3078. else
  3079. begin
  3080. fMinWidth := fRatio.Horizontal;
  3081. fMinHeight := fRatio.Vertical;
  3082. end;
  3083. imgPresent :=not(fImageBitmap.Empty);
  3084. //Change all the Crop Area with KeepAspectRatio=bParent
  3085. for i:=0 to rCropAreas.Count-1 do
  3086. begin
  3087. curCropArea :=rCropAreas[i];
  3088. if (curCropArea<>nil) and (curCropArea.KeepAspectRatio=bParent) then
  3089. begin
  3090. if fKeepAspectRatio
  3091. then curCropArea.CopyAspectFromParent;
  3092. if imgPresent
  3093. then ApplyRatioToArea(curCropArea);
  3094. end;
  3095. end;
  3096. if imgPresent
  3097. then Render;
  3098. Invalidate;
  3099. end;
  3100. end;
  3101. procedure TBGRAImageManipulation.setEmptyImage(AValue: TBGRAEmptyImage);
  3102. begin
  3103. rEmptyImage.Assign(AValue);
  3104. end;
  3105. procedure TBGRAImageManipulation.setMinHeight(const Value: integer);
  3106. begin
  3107. if (Value <> fMinHeight) then
  3108. begin
  3109. if (Value < fSizeLimits.minHeight) then
  3110. begin
  3111. fMinHeight := fSizeLimits.minHeight;
  3112. end
  3113. else
  3114. begin
  3115. if (Value > fSizeLimits.maxHeight) then
  3116. begin
  3117. fMinHeight := fSizeLimits.maxHeight;
  3118. end
  3119. else
  3120. begin
  3121. fMinHeight := Value;
  3122. end;
  3123. end;
  3124. if (fKeepAspectRatio) then
  3125. begin
  3126. // Recalculates the width value based on height
  3127. fMinWidth := Trunc(fMinHeight * (fRatio.Horizontal / fRatio.Vertical));
  3128. end;
  3129. Render;
  3130. Invalidate;
  3131. end;
  3132. end;
  3133. procedure TBGRAImageManipulation.setMinWidth(const Value: integer);
  3134. begin
  3135. if (Value <> fMinWidth) then
  3136. begin
  3137. if (Value < fSizeLimits.minWidth) then
  3138. begin
  3139. fMinWidth := fSizeLimits.minWidth;
  3140. end
  3141. else
  3142. begin
  3143. if (Value > fSizeLimits.maxWidth) then
  3144. begin
  3145. fMinWidth := fSizeLimits.maxWidth;
  3146. end
  3147. else
  3148. begin
  3149. fMinWidth := Value;
  3150. end;
  3151. end;
  3152. if (fKeepAspectRatio) then
  3153. begin
  3154. // Recalculates the height value based on width
  3155. fMinHeight := Trunc(fMinWidth * (fRatio.Vertical / fRatio.Horizontal));
  3156. end;
  3157. Render;
  3158. Invalidate;
  3159. end;
  3160. end;
  3161. procedure TBGRAImageManipulation.setSelectedCropArea(AValue: TCropArea);
  3162. var
  3163. oldSelected :TCropArea;
  3164. begin
  3165. if rSelectedCropArea=AValue then Exit;
  3166. oldSelected :=rSelectedCropArea;
  3167. rSelectedCropArea:=AValue;
  3168. Render;
  3169. Invalidate;
  3170. if assigned(rOnSelectedCropAreaChanged)
  3171. then rOnSelectedCropAreaChanged(Self, oldSelected);
  3172. end;
  3173. { ============================================================================ }
  3174. { =====[ Event Control ]====================================================== }
  3175. { ============================================================================ }
  3176. //Controllare tutte e 3
  3177. procedure TBGRAImageManipulation.MouseDown(Button: TMouseButton;
  3178. Shift: TShiftState; X, Y: integer);
  3179. var
  3180. WorkRect: TRect;
  3181. ACursor :TCursor;
  3182. begin
  3183. // Call the inherited MouseDown() procedure
  3184. inherited MouseDown(Button, Shift, X, Y);
  3185. // Find the working area of the control
  3186. WorkRect := getWorkRect;
  3187. // If over control
  3188. if (((X >= WorkRect.Left) and (X <= WorkRect.Right) and
  3189. (Y >= WorkRect.Top) and (Y <= WorkRect.Bottom)) and
  3190. (Button = mbLeft) and (not (ssDouble in Shift))) then
  3191. begin
  3192. // If this was the left mouse button and nor double click
  3193. fMouseCaught := True;
  3194. fStartPoint := Point(X - WorkRect.Left, Y - WorkRect.Top);
  3195. //rNewCropArea :=nil;
  3196. SelectedCropArea :=Self.isOverAnchor(fStartPoint, fAnchorSelected, {%H-}ACursor);
  3197. if (SelectedCropArea<>nil)
  3198. then fStartArea :=SelectedCropArea.ScaledArea;
  3199. if (fAnchorSelected = [NORTH, SOUTH, EAST, WEST])
  3200. then begin // Move the cropping area
  3201. fStartPoint :=Point(X - SelectedCropArea.ScaledArea.Left, Y-SelectedCropArea.ScaledArea.Top);
  3202. end
  3203. else begin // Resize the cropping area from cornes
  3204. // Get the coordinate corresponding to the opposite quadrant and
  3205. // set into fStartPoint
  3206. if ((fAnchorSelected = [NORTH]) or (fAnchorSelected = [WEST]) or
  3207. (fAnchorSelected = [NORTH, WEST]))
  3208. then fStartPoint := Point(SelectedCropArea.ScaledArea.Right, SelectedCropArea.ScaledArea.Bottom);
  3209. if (fAnchorSelected = [SOUTH, WEST])
  3210. then fStartPoint := Point(SelectedCropArea.ScaledArea.Right, SelectedCropArea.ScaledArea.Top);
  3211. if ((fAnchorSelected = [SOUTH]) or (fAnchorSelected = [EAST]) or
  3212. (fAnchorSelected = [SOUTH, EAST]))
  3213. then fStartPoint := Point(SelectedCropArea.ScaledArea.Left, SelectedCropArea.ScaledArea.Top);
  3214. if (fAnchorSelected = [NORTH, EAST])
  3215. then fStartPoint := Point(SelectedCropArea.ScaledArea.Left, SelectedCropArea.ScaledArea.Bottom);
  3216. end;
  3217. end;
  3218. end;
  3219. procedure TBGRAImageManipulation.MouseMove(Shift: TShiftState; X, Y: integer);
  3220. var
  3221. needRepaint: boolean;
  3222. WorkRect: TRect;
  3223. newCoords: TCoord;
  3224. Direction: TDirection;
  3225. Bounds: TRect;
  3226. {%H-}overCropArea :TCropArea;
  3227. ACursor :TCursor;
  3228. procedure newSelection;
  3229. begin
  3230. // Starts a new selection of cropping area
  3231. try
  3232. Cursor := crCross;
  3233. fEndPoint := Point(X - WorkRect.Left, Y - WorkRect.Top);
  3234. // Copy coord
  3235. with newCoords do
  3236. begin
  3237. x1 := fStartPoint.X;
  3238. y1 := fStartPoint.Y;
  3239. x2 := fEndPoint.X;
  3240. y2 := fEndPoint.Y;
  3241. end;
  3242. // Determine direction
  3243. Direction := getDirection(fStartPoint, fEndPoint);
  3244. // Apply the ratio, if necessary
  3245. newCoords := ApplyRatioToAxes(newCoords, Direction, Bounds, rNewCropArea);
  3246. // Determines minimum value on both axes
  3247. // new Area have KeepAspectRatio setted to bParent by default
  3248. newCoords := ApplyDimRestriction(newCoords, Direction, Bounds, fKeepAspectRatio);
  3249. if (rNewCropArea = Nil)
  3250. then begin
  3251. rNewCropArea :=addScaledCropArea(Rect(newCoords.x1, newCoords.y1, newCoords.x2, newCoords.y2));
  3252. SelectedCropArea :=rNewCropArea;
  3253. end
  3254. else rNewCropArea.ScaledArea :=Rect(newCoords.x1, newCoords.y1, newCoords.x2, newCoords.y2);
  3255. finally
  3256. needRepaint := True;
  3257. end;
  3258. end;
  3259. procedure moveCropping;
  3260. begin
  3261. Cursor := crSizeAll;
  3262. // Move the cropping area
  3263. try
  3264. WorkRect :=SelectedCropArea.ScaledArea;
  3265. WorkRect.Left :=fEndPoint.X-fStartPoint.X; //fStartPoint is Relative to CropArea
  3266. WorkRect.Top :=fEndPoint.Y-fStartPoint.Y;
  3267. //Out of Bounds check
  3268. if (WorkRect.Left<0)
  3269. then WorkRect.Left :=0;
  3270. if (WorkRect.Top<0)
  3271. then WorkRect.Top :=0;
  3272. if (WorkRect.Left+fStartArea.Width>Bounds.Right)
  3273. then WorkRect.Left :=Bounds.Right-fStartArea.Width;
  3274. if (WorkRect.Top+fStartArea.Height>Bounds.Bottom)
  3275. then WorkRect.Top :=Bounds.Bottom-fStartArea.Height;
  3276. WorkRect.Width :=fStartArea.Width;
  3277. WorkRect.Height:=fStartArea.Height;
  3278. SelectedCropArea.ScaledArea :=WorkRect;
  3279. finally
  3280. needRepaint := True;
  3281. end;
  3282. end;
  3283. procedure resizeCropping;
  3284. begin
  3285. // Resize the cropping area
  3286. try
  3287. if ((fAnchorSelected = [EAST]) or (fAnchorSelected = [WEST]))
  3288. then Cursor := crSizeWE
  3289. else if (NORTH in fAnchorSelected)
  3290. then begin
  3291. if (WEST in fAnchorSelected)
  3292. then Cursor := crSizeNW
  3293. else if (EAST in fAnchorSelected)
  3294. then Cursor := crSizeNE
  3295. else Cursor := crSizeNS;
  3296. end
  3297. else begin
  3298. if (WEST in fAnchorSelected)
  3299. then Cursor := crSizeSW
  3300. else if (EAST in fAnchorSelected)
  3301. then Cursor := crSizeSE
  3302. else Cursor := crSizeNS;
  3303. end;
  3304. // Copy coord
  3305. with newCoords do
  3306. begin
  3307. x1 := fStartPoint.X;
  3308. y1 := fStartPoint.Y;
  3309. if (fAnchorSelected = [NORTH]) then
  3310. begin
  3311. x2 := fEndPoint.X - Abs(SelectedCropArea.ScaledArea.Right - SelectedCropArea.ScaledArea.Left) div 2;
  3312. y2 := fEndPoint.Y;
  3313. end
  3314. else
  3315. if (fAnchorSelected = [SOUTH]) then
  3316. begin
  3317. x2 := fEndPoint.X + Abs(SelectedCropArea.ScaledArea.Right - SelectedCropArea.ScaledArea.Left) div 2;
  3318. y2 := fEndPoint.Y;
  3319. end
  3320. else
  3321. if (fAnchorSelected = [EAST]) then
  3322. begin
  3323. x2 := fEndPoint.X;
  3324. y2 := fEndPoint.Y + Abs(SelectedCropArea.ScaledArea.Bottom - SelectedCropArea.ScaledArea.Top) div 2;
  3325. end
  3326. else
  3327. if (fAnchorSelected = [WEST]) then
  3328. begin
  3329. x2 := fEndPoint.X;
  3330. y2 := fEndPoint.Y - Abs(SelectedCropArea.ScaledArea.Bottom - SelectedCropArea.ScaledArea.Top) div 2;
  3331. end
  3332. else
  3333. begin
  3334. x2 := fEndPoint.X;
  3335. y2 := fEndPoint.Y;
  3336. end;
  3337. end;
  3338. // Determine direction
  3339. Direction := getDirection(fStartPoint, fEndPoint);
  3340. // Apply the ratio, if necessary
  3341. newCoords := ApplyRatioToAxes(newCoords, Direction, Bounds, SelectedCropArea);
  3342. // Determines minimum value on both axes
  3343. newCoords := ApplyDimRestriction(newCoords, Direction, Bounds, SelectedCropArea.getRealKeepAspectRatio);
  3344. SelectedCropArea.ScaledArea := Rect(newCoords.x1, newCoords.y1, newCoords.x2, newCoords.y2);
  3345. finally
  3346. needRepaint := True;
  3347. end;
  3348. end;
  3349. begin
  3350. // Call the inherited MouseMove() procedure
  3351. inherited MouseMove(Shift, X, Y);
  3352. // Set default cursor
  3353. Cursor := crDefault;
  3354. // Find the working area of the component
  3355. WorkRect := GetWorkRect;
  3356. // If the mouse was originally clicked on the control
  3357. if fMouseCaught
  3358. then begin
  3359. // Assume we don't need to repaint the control
  3360. needRepaint := False;
  3361. // Determines limite values
  3362. Bounds := getImageRect(fResampledBitmap);
  3363. // If no anchor selected
  3364. if (fAnchorSelected = [])
  3365. then newSelection
  3366. else begin
  3367. // Get the actual point
  3368. fEndPoint := Point(X - WorkRect.Left, Y - WorkRect.Top);
  3369. // Check what the anchor was dragged
  3370. if (fAnchorSelected = [NORTH, SOUTH, EAST, WEST])
  3371. then moveCropping
  3372. else resizeCropping;
  3373. end;
  3374. // If we need to repaint
  3375. if needRepaint then
  3376. begin
  3377. SelectedCropArea.CalculateAreaFromScaledArea;
  3378. if assigned(rOnCropAreaChanged)
  3379. then rOnCropAreaChanged(Self, SelectedCropArea);
  3380. // Invalidate the control for repainting
  3381. Render;
  3382. Refresh;
  3383. end;
  3384. end
  3385. else begin
  3386. // If the mouse is just moving over the control, and wasn't originally click in the control
  3387. if ((X >= WorkRect.Left) and (X <= WorkRect.Right) and
  3388. (Y >= WorkRect.Top) and (Y <= WorkRect.Bottom)) then
  3389. begin
  3390. // Mouse is inside the pressable part of the control
  3391. Cursor := crCross;
  3392. fAnchorSelected := [];
  3393. fEndPoint := Point(X - WorkRect.Left, Y - WorkRect.Top);
  3394. // Verifies that is positioned on an anchor
  3395. ACursor := crDefault;
  3396. overCropArea :=Self.isOverAnchor(fEndPoint, fAnchorSelected, ACursor);
  3397. Cursor :=ACursor;
  3398. end;
  3399. end;
  3400. end;
  3401. procedure TBGRAImageManipulation.MouseUp(Button: TMouseButton;
  3402. Shift: TShiftState; X, Y: integer);
  3403. var
  3404. needRepaint: boolean;
  3405. temp: integer;
  3406. curCropAreaRect :TRect;
  3407. begin
  3408. // Call the inherited MouseUp() procedure
  3409. inherited MouseUp(Button, Shift, X, Y);
  3410. // If the mouse was originally clicked over the control
  3411. if (fMouseCaught) then
  3412. begin
  3413. // Show that the mouse is no longer caught
  3414. fMouseCaught := False;
  3415. // Assume we don't need to repaint the control
  3416. needRepaint := False;
  3417. if (rNewCropArea = Nil)
  3418. then begin
  3419. if (ssAlt in Shift)
  3420. then begin
  3421. SelectedCropArea.ScaledArea :=fStartArea;
  3422. needRepaint :=True;
  3423. end
  3424. end
  3425. else begin // Ends a new selection of cropping area
  3426. if (ssAlt in Shift)
  3427. then begin
  3428. delCropArea(rNewCropArea);
  3429. rNewCropArea :=Nil;
  3430. needRepaint :=False;
  3431. end
  3432. else begin
  3433. SelectedCropArea :=rNewCropArea;
  3434. rNewCropArea :=Nil;
  3435. curCropAreaRect :=SelectedCropArea.ScaledArea;
  3436. if (curCropAreaRect.Left > curCropAreaRect.Right) then
  3437. begin
  3438. // Swap left and right coordinates
  3439. temp := curCropAreaRect.Left;
  3440. curCropAreaRect.Left := curCropAreaRect.Right;
  3441. curCropAreaRect.Right := temp;
  3442. end;
  3443. if (curCropAreaRect.Top > curCropAreaRect.Bottom) then
  3444. begin
  3445. // Swap Top and Bottom coordinates
  3446. temp := curCropAreaRect.Top;
  3447. curCropAreaRect.Top := curCropAreaRect.Bottom;
  3448. curCropAreaRect.Bottom := temp;
  3449. end;
  3450. needRepaint :=True;
  3451. end;
  3452. end;
  3453. fAnchorSelected := [];
  3454. // If we need to repaint
  3455. if needRepaint then
  3456. begin
  3457. SelectedCropArea.CalculateAreaFromScaledArea;
  3458. if assigned(rOnCropAreaChanged)
  3459. then rOnCropAreaChanged(Self, SelectedCropArea);
  3460. // Invalidate the control for repainting
  3461. Render;
  3462. Refresh;
  3463. end;
  3464. end;
  3465. end;
  3466. procedure TBGRAImageManipulation.DoContextPopup(MousePos: TPoint; var Handled: Boolean);
  3467. var
  3468. xAnchorSelected :TDirection;
  3469. xCursor :TCursor;
  3470. mouseCropArea:TCropArea;
  3471. begin
  3472. if Assigned(rOnContextPopup) then
  3473. begin
  3474. mouseCropArea :=Self.isOverAnchor(MousePos, xAnchorSelected, {%H-}xCursor);
  3475. rOnContextPopup(Self, mouseCropArea, xAnchorSelected, MousePos, Handled);
  3476. end;
  3477. end;
  3478. { ============================================================================ }
  3479. { =====[ Register Function ]================================================== }
  3480. { ============================================================================ }
  3481. {$IFDEF FPC}
  3482. procedure Register;
  3483. begin
  3484. RegisterComponents('BGRA Controls', [TBGRAImageManipulation]);
  3485. end;
  3486. {$ENDIF}
  3487. end.