bgraimagemanipulation.pas 129 KB

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