DELPHI4ProgrammingManual ¦ Jan2022 UNIVERSUMS HISTORIA | 2011V4 ¦ 2022I18 | aproduction | Senast uppdaterade version: 2022-09-10 YMD ¦  HumanRight is a knowledge domain  

 

content · webbSÖK äMNESORD på denna sida Ctrl+F   SubjectINDEX      all files    helpStart

 

 

 

Elementär FlytbildsTeknik flytbildsläget16;64-felet i DELPHI4 | EditColors | TabellHIMRV |  Se även BILDHANTERING [PictureHANDLING/picture manipulation] allmänt

 

GENERAL IMAGE HANDLING IN Delphi4Test2022 ¦ ImportExport

Foto:  15Sep2014  E32  Bild112  · NikonD90 ¦ NaturenRef

 

 

Hur flytbildsläget fungerar med ovanstående illustrations begrepp beskrivs också utförligt i

FLYTBILDSFLAGGOR.

Exempel, 4Sep2014

I tur och ordning — detta htm-dokument:

DelphiBegin ¦ DelphiKOD ¦ ProgramFÖNSTRET ¦ KOMPONENTER ¦ ProjektKatalogen ¦ ExeIcon ¦ Label1 ¦ ProgramENHETER ¦ Caption — FönsterNamn ¦ BildKODteknik ¦ Alla 4 Procedurer ¦ Peken ¦ Ctrl+V ¦ Resultat1 ¦ FlytBildsTEKNIK ¦ F5 ¦

BildEXPORT — Ctrl+I | PilotDISPLAY | MARKÖRACCELERATORER | Resultat2 | Flytbilden | RitaMusHÖGER | MoveMore | MultiWin ¦ Flytsätten ¦ BGOF ¦ INVERTERA ¦ RGB palett ¦ FEL1664 ¦ TBitMapInfoHeader ¦ EditColors ¦

Unit1A ¦ StreckRektangeln ¦ ClipCursor ¦ PROGRAMDIVERGENS ¦ HELPex ¦ HIMRV | Zoom | WriteText | TextIn | RamToning | CommanderDoit | Label1 | Label1 | Label1 | Label1 ¦

http://www.universumshistoria.se/AAALib/DELPHI4/HTM/AD4a1Begin.htm

29OKT2014

Behandlat | P2:

FlytBildsFunktionerna MoveAND|Copy|SkipWHITE — Flytsätten

RitaMusHöger — motsvarar »pennan i bröstfickan»: alltid tillgänglig på bildytan

BildHantering: Invertera|Rotera90°|VändHorisontellt¦Vertikalt|Duplicera|Radera|Kopiera — TabellHIMRV

RGB|HSBRGB-palett med

EditColors — färgredigering och analys

Zoomning

WriteText — snabb, effektiv text på bild

TextIn — TrueType-text mot godtyckliga bakgrunder

RamToning — sammansmältning med flera bilder

CommanderDoit — ritaVerktyg

 

 

Elementära datorhjälpmedel — flytta kombinera sammanställa bilddetaljer UTAN NÅGON DIREKT BILDBEHANDLINGSTEKNIK

Uppbyggnad av ett flytbildsblock i Delphi4

Hel programform i detalj — exakt pixelpositionering — se tabellen med alla flytbildskommandon i TabellHIMRV

Arkivet 2011 ¦ 14:

 

Vidareutvecklat Från DELPHI 4 Test 2011 — BellDHARMA  for UniverseHistory — HUVUDDELEN AV VERKTYGEN MAN BEHÖVER FÖR AVANCERAD DATORANVÄNDNING I TEXT OCH BILD

 

      Delphi4 Programming Manual 2014|a1

   — elementär flytbildsteknik

AllmänDistribution DELPHI4 MANUAL BellDHARMA Sep2014 — T2014

 

DISPOSITION T2014 |  TeckenförklaringarAllmänt: MusKLICKVänster | Höger:  |   — RullaMushjulet FRÅN | MOTDig:  |

 

 

Uppbyggnad — flytbildsblock:

1. Image1: synlig bildytan (»titten») vi alltid tittar på på bildskärmen;

2. Image2: gömd flytbilden = urklippskopian vi tar in och som ska flyttas — flyta — omkring på, i, över, genom titten;

3. Image3: gömd KOPIATORN som räddar undan en tittkopia INNAN Flyten läggs på:

 

 Nedan beskrivs detaljerna hur man kan förverkliga verktygen: Panel1 med Image1, Image2 och Image3

 

— För varje ny position lägger Im3 FÖRST tillbaka föregående uttag på TittOriginalets plats; SEDAN tar Im3 in en ny kopia av Tittstället där Flyten kommer att läggas på i nästa snäpp — totalt med TYP Ctrl+Pilar (eller konventionell musflyttning [jag använder själv aldrig musen för bildflyttning i mina program; Ctrl+ NumPad 1 2 3 5 9 sätter flyttsteg om 1 5 20 Halvsida Helsida pixels: helt underbar funktion: snabbt, exakt]) att den intagna urklippskopian FLYTER obehindrat över Titten utan att ändra något.

 

Efter min erfarenhet:

1. Lägg ALDRIG en SYNLIG ImageKomponent i DELPHI(4) direkt på en formyta = själva programfönsterkomponenten. Gör man det upptäcker man snart ett hedjlöst FLIMRANDE vid fönsterändringar;

2. Reservera ALLTID en PanelKomponent som bas till en SYNLIG ImageKomponent för att få störningsfria övergångar i alla möjliga sammanhang: Panel1 med Image1 osv.

3. Principen är densamma för Labels (textRemseKomponenter) på Image: sporadiska mindre flimmer uppkommer om LabelKomponenten genomgår upprepade ändringar.

— Det gäller även till viss del Labels på Panels — ett fenomen i DELPHI(4) man får försöka kämpa med och hitta lösningar till om det börjar jävlas särskilt. Generellt sett är dock det inget problem.

— Det generella sättet att HELT eliminera flimmertyper mellan olika pålagda komponenter är helt enkelt att skippa dem och att skriva KOD PÅ Image direkt: I Delphi(4) finns (nämligen) ingenting man INTE kan göra med en bild: text, grafik, foto, rit. Men dessa är bara mina egna erfarenheten. Testa själv är bäst.

 

DelphiBegin:

Här: DELPHI1 från Start (gavs ut som gratisprogram från 1997):

 

Objekt:

 

 

 

— DELPHI4 ser ut ungefär på samma sätt, bara med skillnaden att det finns något fler komponenter samt att hela systemet stödjer 32-bitars databusstruktur mot endast 16 bitar i Delphi1.

— Komponenter dras från Raddan med tabbade komponentlister till Form1 — programfönstrets bildyta — och förses med separat KOD i Edit-fönstret som här ligger bakom Form1 (Man växlar mellan dessa Med F12 och till ObjectInspector med F11).

— Form1-fönstret är samma som (det kommande) programfönstret, och man fyller formen med komponenter: Knappar, Paneler Bildytor (Image), Textboxar, Memoboxar, Listboxar, osv. — eller att man kan skriva KOD för alla dessa.

— Bakom Form1-fönstret [F12] ligger den avgörande centrala CodeEdit-fönstret (Unit1, Unit2, Unit ...); Programkod skrivs där i Borland Turbo Pascal (Kodsidorna eller Units sparas sedan i Projektkatalogen som *.pas-filer = vanliga textfiler med specialfiländelse: sedan DELPHI4 kompilerat [Ctrl+F9] dessa till en fungerande exe-fil, är dessa kodsidor bara »rester» — som kan byggas på, vidare med nya kompileringar [Ctrl+9] osv.).

— Se även i INSTALLATIONSANVISNINGAR med StartExempel hur allt ser ut från början i DELPHI4, och hur man kan göra grundinställningar som garanterar en maximalt smidig funktion från början.

 

DelphiKod:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Så här ser KodFönstret ut i DELPHI4 då ett helt nytt projekt påbörjats (Alt+File), New Application:

 

 

KodFönstren i DELPHI4 finns förtecknade i ViewUnit och anropas med Ctrl+F12,

 

 

Projekten i DELPHI4 — obs: öppnas på SENAST ÖPPNADE — finns förtecknade i OpenProject med Ctrl+F11,

 

 

ProjektFilen man ska leta efter ock klicka på har filändelsen dpr med DELPHI4-logon som den gula AntikPortalen:

 

 

INGENTING AV DESSA BERÖR DOCK NYBÖRJARENS FÖRSTA BEKANTSKAP. Ovanstående kommer senare, vid behov.

 

— SEDAN man initierat flera olika projekt, finns ett alternativt sätt att växla mellan flera olika projekt — bara ett projekt åt gången får vara öppet i DELPHI4:

— Tryck Alt+F+R(eopen); DELPHI4 visar en lista med de senast öppnade projekten, och det är bara att välja direkt därifrån.

 

 

Programfönstret:

— Ingen kod finns ännu inskriven. Men snart.

— Jag har här försökt trycka på F9 = KÖR för att tvinga DELPHI4 att spara upp det nya projektet på (en av mig anvisad) plats (Katalog P2) och ett nytt projektnamn (P2): DELPHI4 visar automatiskt dialogrutor för detta (fyll i och fortsätt — efter denna första initiering sköter sedan DELPHI4 automatiskt uppdateringar på den anvisade platsen för varje ny körning;

Se mina exempel på grundinställningar i INSTALLATIONSANVISNINGAR, AutoSaveOptions).

Jag har här (Object Inspector, tangent F11) satt Form1-programfönstrets mått XY till 200;500pixel:

 

 

Komponenter:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Nu börjar vi med att lägga in en Panel1, en Image1, Image2, och en Image3:

— vi klickar på komponenterna på Tabbflikarna,

 

 

släpper komponenterna på Form1-ytan, och DELPHI4 skriver automatiskt in Grunddata åt oss;

Ctrl+Pilar flyttar komponenterna 1 pixel på formytan och Ctrl+Shift+Pilar flyttar 8pixels.

Shift+PilUppNer reglerar höjd och Shift+PilVäHö reglerar längd. Dessa kan också skrivas in med värden direkt i Object Inspector.

— Notera också Anchors på ObjectInspector: Dubbelklicka på Achorsrutan och ställ in önskat. Standard är att varje komponent VID FÖNSTERÄNDRINGAR ligger LÅST TopLeft. Men vi kan ändra dessa så att t.ex. en bottenpanel alltid fortsätter ligga LeftBottom med fönsterändringar.

— Om vi redan har en viss komponent på formytan och vill ha flera av den typen, går det också att köra Ctrl+C, sätta fokus på formytan (annars blir den valda komponenten Parent) och ta in en redan formaterat (Object Inspector) komponent med samma mått och egenskaper:

 

 

Avgörande viktigt för att bildintagen ska fungera korrekt är att vi på Object Inspector (F11) för Image2 och Image3 sätter egenskapen AutoSize=True men False på Image1. För att säkra grundinställningarna kan vi skriva in dem i kod på FormCreate — nedan också med en mindre aggressivt orange bakgrundsfärg till Label1 (beskrivs längre fram), samt en alternativ teckensnittsfärg (mörkviolett) för att exemplifiera personliga val:

 

       Left:= (Screen.Width -  Width)  div 2;

       Top:=  (Screen.Height - Height) div 2;

       Image1.AutoSize:= False;

       Image2.AutoSize:= True;

       Image3.AutoSize:= True;

       Label1.Color:= RGB(255,238,221);

       Label1.Font.Color:= clPurple;

 

De bägge översta raderna garanterar att ProgramFönstret alltid öppnas mitt på bildskärmen.

 

 

Vad är ”clPurple”? Placera markören i DELPHI4:s kodfönster i ordet och tryck F1: en  separat hjälpruta kommer fram från den omfattande hjälpen och beskriver detaljerna. Grundfärger har namn (clBlack, clWhite, clBlue, clNavy, clPuple, ...), medan färger generellt kan anges i RGB eller Hexkod.

 

Panel1 som Parent till Image1, och två ytterligare Image separat (som kommer att fungera gömda) Image2 och Image3.

Notera egenskaperna Visible med True och False på komponenterna i Object Inspector: Panel1 och Image1 ska vara Visible=Synliga hela tiden, medan Image2 och 3 ska vara Visible=False; Ctrl+Enter ändrar True/False direkt med vald komponent och Visible i Object Inspector;

 

För att markera gemensamma egenskaper för flera komponenter åt gången: Klicka på/markera första komponenten; håll

1. ner Shift och klicka på alla övriga som önskas;

eller

2. MusVäNer och dra streckrektangeln över alla;

Gå sedan till Object Inspector och sätt önskad egenskap.

 

Med de ovan införda ändringarna, trycker jag nu på Ctrl+F9 = DELPHI4 KOMPILERAR = skapar en körbar EXE-programfil som vi sedan kör med F9 — eller bara direkt med F9 (Delphi kompilerar då först automatiskt — och kör direkt om inga fel rapporteras).

— KodFönstrets information i Unit1 har då ändrat utseende till följande:

TypeBlocket

 

Elementär flytbildsteknik — grundexempel i DELPHI4

 

— Vi ser att en ny Instans ”ExtCtrls” har tillkommit — Delphi(4) lägger INTE ALLTID automatiskt till sådana i de (mycket) speciella fallen. Men kunskaperna om dessa detaljer brukar följa med den löpande informationen, och vi får hur som helst inblick i hemligheterna vart efter.

— Dessutom har DELPHI4 lagt till de nu införda komponenterna [Avancerade programmerare kan skriva komponenter själva i VirtuellKod (som man gör i C++) typ Image1.Picture.Create förutsatt man också städar upp efter sig innan programslut för att inte göra datorn sur för andra som vill ha rent och snyggt i minnet].

 

ProjektKaralogen:

— PROJEKTKATALOGEN  (P2) innehåller nu samtliga projektfiler enligt

 

 

Ico-filen har jag själv lagt in (från gratisversioner på webben) för att vi senare ska testa hur man lägger in en sådan längst upp i programfönstret. Ikonen som syns ovan för exe-filen är den som DELPHI4 har som standard.

 

 

ExeIcon:

KODEn som krävs för att ladda in en alternativ icon I PROGRAMFÖNSTRETS RUBRIKLIST är

 

Application.Icon.LoadFromFile('FilNamnet');

 

För att också få in en alternativ icon i exe-filens iconInstans,

 

tryck Ctrl+Shift+F11 från DELPHI4, fliken Application, LoadIcon, browsa fram till Projektkatalogen (om iconfilen finns där) och tryck Öppna. DELPHI4 lägger då in den ikonen automatiskt i exe-filen,

 

— DELPHI4 förutsätter automatiskt OM INGET ANNAT ANGES att FILNAMN UTAN SPECIELLA KATALOGREFERENSER ligger i samma katalog som exe-filen.

— OM vi använder t.ex. andra instanser (typ sökprogram som är ute och snurrar på andra enheter) KAN vi i VARJE läge försäkra os om att DELPHI4 hittar HemTillVåranProjektKatalog genom programkoden

 

ExtractFilePath(Application.ExeName);

Label1:

 

— Vi ser redan nu, omgående, att VI MÅSTE HA NÅGOT ENKELT VERKTYG ATT TESTA PÅSTÅENDENA från den här författaren med, se att och HUR det verkligen fungerar

— Vi gör det genom att lägga till en Label1 (på en Panel2) på Form1,

 

 

— Synliga Namnet på Panel2 kan vi gömma undan på Object Inspector genom att lämna Caption-boxen tom.

Färgvalet för Label1 görs på Object Inspector, Color — endast OMVÄND HEXkod accepteras. Exempel:

— Här är den ljusorangea RGB-färgen 255,224,184 och motsvarande HEX FF E0 B8. DELPHI4 vill emellertid ha hexformen på omvända fasonen med inledande två nollor samt ett dollartecken

$00b8e0ff som gillas av Delphi med ändringen $00B8E0FF om man pilar ut, med motsvarande verkställd ändrad färg. Kan också justeras under programkörning med Label1.Color:= RGB(255,224,184);

Avsluta programmet med Alt+F4 eller vänsterklicka på krysset.

Eliminera ScrollBars från fönstret via Object Inspector

 

 

genom dubbelklick på +VertScrollBar och +HorzScrollBar och sätt (Ctrl+Enter) False på Visible.

 

 

och vilken skrivetikett vi sedan kan nå i olika skrivuppgifter (främst, om inget annat) genom att (»baksidan» på Object Inspector; F11, Ctrl+Tab, OnClick, Ctrl+Enter, se nedan) aktivera ett FormKlick-kodblock i DELPHI4:

— När vi klickar på Formytan, går programexekveringen DIT, och vi kan skriva in typ

 

Label1.Caption:= ExtractFilePath(Application.ExeName);

 

så att vi VET att den här författaren inte enbart pratar i nattmössan.

— »Baksidan» på Object Inspector — F11, Ctrl+Tab OnClick, Ctrl+Enter — gör att DELPHI4 automatiskt lägger till följande på Unit1;

Sist i type-blocket:

procedure FormClick(Sender: TObject);

Längst ner, närmast över end.:

procedure TForm1.FormClick(Sender: TObject);

begin

 

end;

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Skriver vi inte in något där, och kompilerar (Ctrl+F9) försvinner den inlagda proceduren — DELPHI4:s sätt att AutoRensa procedurer som inte innehåller någon verkställande programkod — detta gäller dock INTE procedurer som vi själva sedan skriver, bara DELPHI4-komponent och händelserelaterade dito. Bara sagt i parentes.

— Vi gör nu tillägget

 

procedure TForm1.FormClick(Sender: TObject);

begin

Label1.Caption:= ExtractFilePath(Application.ExeName);

ClipBoard.AsText:= Label1.Caption;

end;

Programenheter:

Tillägget med ClipBoard

 

Vi måste lägga till en Unit ”ClipBrd” till uses-blocket först, annars reagerar DELPHI4 så här om vi försöker kompilera (Ctrl+F9) eller köra direkt (F9):

 

 

FELMEDDEELANDEFÖNSTRET i DELPHI4 nederst kan vara (lite) påfrestande ibland med sina »upplysningar»: Man glömmer ett och annat, då och då i Kodningen — och lär sig med tiden uppskatta anmärkningarna: Delphi har ALLTID rätt (på ett eller annat sätt) MEN VI KANSKE INTE ALLTID FÖRSTÅR DET MED EN GÅNG, VAD DET ÄR FÖR FEL VI GÖR.

— Därför är det avgörande viktigt att nybörjaren får se EXEMPEL FRÅN REDAN FUNGERANDE ENHETER: vi vill INTE att nybörjare ska tappa modet för att man inte lyckas med en gång.

— Säkra exempel, redan testade, är avgörande viktiga.

Därifrån kan man sedan testa, modifiera och ändra för att upptäcka eget.

 

Korrekt sätt [Se UnitRubrikerna] (Vi får leta i andra avsnitt efter lösningen, här grundat på erfarenhet och sökningar i vad tillgängligt som finns) blir att först lägga till [DELPHI4 har också tidigare lagt till en enhet (via Label1) StdCtrls]

 

 

gör att jag som författare (och du som testare) slipper skriva av visuellt från Label 1, och bara kan trycka Ctrl+V och få in resultatet hit direkt (Vi kör F9 och vänsterklickar på formytan):

 

C:\Program Files (x86)\Borland\Delphi4\Projects\P2\

 

 

— OK. Nu har vi en INSTANS — FormClick — med vars hjälp vi kan ändra Programikonen. Vi gör det genom att lägga till följande programkod i proceduren FormClick:

 

procedure TForm1.FormClick(Sender: TObject);

begin

Label1.Caption:= ExtractFilePath(Application.ExeName);

ClipBoard.AsText:= Label1.Caption;

Application.Icon.LoadFromFile(

ExtractFilePath(Application.ExeName) + 'ImageReady.ico'

end;

Caption, FönsterNamn

Elementär flytbildsteknik — grundexempel i DELPHI4

Resultatet blev:

 

”Form1” läggs ut som namn på programfönstret automatiskt av DELPHI4 om inget annat anges;

— På FormCreate, eller när som helst under körning,  kan vi skriva in annat enligt det enkla

Caption:= 'Annat';

 

För vi ska få behålla den ikonen, måste den tas in (eller på annat sätt införlivas med programmet) varje gång vi startar programmet.

— Det finns särskilda RESOURCE-files för ändamålet. Men jag har ALDRIG använt dessa (på grund av vissa äventyrligheter), och tänker inte heller uppmana någon att använda sådana (det bara förkrångligar och introducerar mera än nödvändigt — men webben kanske har Delphientusiaster som [med glädje] talar om hur den resursen ska användas).

— Min lösning är att lägga ApplicationIcon.koden i ett särskilt startblock OnCreate (F11, Events, OnCreate, Ctrl+Enter):

 

DELPHI4 lägger automatiskt till (tillsammans med motsvarande procedurdeklaration i början av Unit1 under Type-blocket)

 

procedure TForm1.FormCreate(Sender: TObject);

begin

 

end;

Och vi skriver nu in

procedure TForm1.FormCreate(Sender: TObject);

var

  S: string;

begin

  S:= ExtractFilePath(Application.ExeName);

  {Extract avslutas alltid automatiskt med en BackSlash\.}

  S:= S + 'ImageReady.ico';

  if FileExists(S) then

  Application.Icon.LoadFromFile(S);

end;

 

När vi kör programmet härifrån i fortsättningen, kommer den valda ikonen alltid att framträda direkt med programfönstret.

— OM ikonfilen händelsevis inte skulle finnas på plats (if FileExists ...), hoppas bara rutinen över och standardikonen kommer fram. Utan if-satsen uppstår fel, eventuellt programavbrott, vilket vi ALDRIG ens vill se röken av: var frikostig med IF:s och Excepts och TryFinallyEnd-block — eller bara ”if not ... then exit;”: programmet går då bara ur aktuell procedur och ignorerar allt under den kommandoposten. Vi kommer säkert dit mera.

 

BildKODteknik

Åter till Image123

 

Nu kommer vi till det verkligt Avancerade i DELPHI4:

— Konsten att få Image1 att hänga med i svängarna med programfönstrets storleksändring — alltså när man börjar dra i fönsterkanterna eller kör

Alt+Space, Storleksändra.

— Jag vet inte hur mycken tid och möda jag lyckades lägga ner på den den, ändra från början med (underbara Delphi1) innan den fullständigt flimmerfria lösningen infann sig:

   Om inget annan lösning finns — kanhända någon Delphientusiast har den, men den är i så fall här helt okänd:

 

HEMLIGHETEN ligger i att AVSTÄMMA Image1 med en TBitMap — samt ”trixa” med en extra Procedur (CoFormCreate) tillsammans med ordinära OnResize (F11, OnResize, Ctrl+Enter):

 

Så här ser min lösning ut:

Närmast under TypeBlocket, en separat införd CoFormCreate. När den sedan skrivs längre ner i aktuellt verkställande kodblock I EN UNIT SOM HAR EN ASSOCIERAD FORM (här Form1) måste den skrivas ”Procedure TForm1.CoFormCreate;

 

    Procedure CoFormCreate;

    procedure FormClick(Sender: TObject);

    procedure FormCreate(Sender: TObject);

    procedure FormResize(Sender: TObject);

Längre ner, här alla 4 procedurer:

Alla4

Elementär flytbildsteknik — grundexempel i DELPHI4

 

procedure TForm1.FormClick(Sender: TObject);

begin

  Label1.Caption:= ExtractFilePath(Application.ExeName);

  ClipBoard.AsText:= Label1.Caption;

end;

 

 Procedure TForm1.CoFormCreate;

 var

   B: TBitMap;

 begin

 {AKTUELLA Image1MÅTTEN:}

  Panel1.BoundsRect:= Rect(0,1,Width-8,Height-Panel2.Height-30);

  Image1.BoundsRect:= Panel1.ClientRect;

 {ANPASSNING:}

  B:= TBitmap.Create;

  try

   B.Width:= Image1.Width;

   B.Height:= Image1.Height;

   Image1.Picture.Assign(B);

  finally

   B.Free;

  end;{endTry}

 {Annars grå yta vid FormResize.}

 end;{endCoFormCreate}

 

procedure TForm1.FormCreate(Sender: TObject);

var

  S: string;

begin

       Image1.AutoSize:= False;

       Image2.AutoSize:= True;

       Image3.AutoSize:= True;

       Label1.Color:= RGB(255,238,221);

       Label1.Font.Color:= clPurple;

  S:= ExtractFilePath(Application.ExeName);

  {Extract avslutas alltid automatiskt med en BackSlash\.}

  S:= S + 'ImageReady.ico';

  if FileExists(S) then

  Application.Icon.LoadFromFile(S);

  Label1.Caption:= 'ProjectP2|Successfully Initialized';

 

  with Image1.Canvas do begin

   Brush.Color:= clWhite;

   FillRect(ClientRect);

  end;

  CoFormCreate;

end;{endFormCreate}

 

procedure TForm1.FormResize(Sender: TObject);

begin

  CoFormCreate;

end;{endFormResize}

 

Försök gärna själv att testa ovanstående med modifikationer för att se om det ev. finns något alternativ att få Image1 med Panel1 att hänga med SNYGGT & PERFEKT & FLIMMERFRITT (Alla WindowsVersioner) då programfönstret storleksändras. I min referens (15 års frekvent användning): Perfekt felfri funktion.

 

 

Label1 före insättning av ljusare orange bakgrund och annan teckensnittsfärg.

Panel2 i botten följer med i fönsterändringen och anpassar sig i längd DÄRFÖR att jag i Object Inspector på Anchors [DUBBELKLICKA på +Anchorboxen, undermenyer kommer fram med alla alternativen] Panel2 har markerat akRight = True: Det mått eller den proportion som Panel2 har på Form1 vid designtillfället, kommer då att bevaras vid alla fönsterändringar.

 

 

— Men nu finns ju inte längre någon Form1-yta kvar tillgänglig att klicka på. Vi skulle ju ha den för olika »experimentella ändamål». Ahh. Ve och fasa.

— Vi kan ordna den saken galant genom Object Inspector på Panel2, nederst ovan:

— På Panel2 OnClick fäller vi via Alt+PilNer fram de alternativ som finns där; FormClick. Vi väljer den.

 

 

— I fortsättningen är nu MusKlick på Panel2 ASSOCIERAT MED musklick på Form1.

 

 

— Mera programkod tillkommer för att MED EN IMPORTERAD bild till Image1 se till att den importen också bibehålls INTAKT med motsvarande fönsterstorleksändringar.

— Vi kommer dit, strax.

— Men först måste vi sätta in den fina BildPeken, särskilt utformad (sedan 14 år tillbaka) och anpassad för precisionsarbeten,

Peken:

 

eller i varje fall se till att det finns en sådan tillgänglig.

— StandardKrysset i DELPHI4 (och andra bildprogram) är sannerligen och nämligen, min mening, inte mycket attg hurra för.

Sä här går det till att lägga till en egen skärmpekare:

1. Den måste ha en egen ico.fil;

2. Den måste ges ett specifikt FastNamn (constant);

3. Den måste laddas in som sådan genom en speciell procedur:

 

Elementär flytbildsteknik — grundexempel i DELPHI4

I ProjektKatalogen (P2) lägger vi först in en särskilt skapad bildskärmsmarkör

 

 

Ikonen för denna markör ändras i Windows sedan man besökt den aktuella katalogen.

— Anledningen till det är här helt okänd. Ständigt nya ikonbilder framträder.

— Den detaljen har dock ingen innebörd för filinnehållet.

 

Vi måste skriva en särskild procedur för det ändamålet

(Först procedurdeklarationen längst upp under TypeBlocket i sedvanlig ordning,

Procedure LoadCursors(S: string; C: Integer)), och sedan aktuellt procedurkodblock i ImplementationSektionens Procedurblock med ”TForm1.” längst fram efter Procedure:

 

Procedure TForm1.LoadCursors(S: string; C: Integer);

var

 P: PChar;

 I: Integer;{HCursor}

begin

 if FileExists(S) then

 begin

   P:= nil;

   try

    P:= StrAlloc(Length(S)+1);

    StrPCopy(P,S);

    I:= LoadCursorFromFile(P);

    Screen.Cursors[C]:= I;

   finally

    StrDispose(P);

   end; {endTry}

 end;{endIfFileExists}

end;{endLoadCursors}

 

samt deklarera den Globala PekarNamnKonstanten (efter UsesBlocket i Unit1), här (MouseCursorNo1) MC1

 

const

{MomentaryCursor:}

  MC1 = 5;

 

På FormCreate (längst ner efter CoFormCreate;) skrivs sedan in:

 

  if FileExists('Cursor1.cur') then begin

    LoadCursors('Cursor1.cur',MC1)

    Image1.Cursor:= MC1;

  else

  Label1.Color:= clAqua;

{Aqua = himmelsKlarblått

 Lime = HavsKlarGrönt

  Finns den inte ges skrikande indikering.}

 

När vi sedan kör igång (F9) med allt det ovan sagda, visar sig vår nya fina kryssmarkör bara när vi för den över Image1:

 

 

Annars den vanliga pilmarkören.

— Alternativa skärmpekare finns i många olika versioner: WindowsSystem har en hel radda inte minst (avancerat) animerade pekare (mus som äter ost, ros som växer fram, häst som springer, etc.) och som man själv kan använda om man så önskar. Se floran i Windows\Cursors.

— Delphis egen PekarBank (sätt markören i ordet Cursor och tryck F1) beskriver (delvis) sammanhangen.

— För att testa Windows olika Pekare, kopiera aktuell Pekare från Windows CursorKatalogen till ProjektKatalogewn (P2), och testa den peken genom att låna kommandona som ovan med *=aktuella PekarNamnet

 

  if FileExists('*.cur') then begin

    LoadCursors('Cursor1.cur',MC1)

    Image1.Cursor:= MC1;

 

Ctrl+V

MEN: Var kommer nu Bilden in i bilden?

MEN: Hur får vi nu in någon bild på Image1 utifrån?

Tangenten PrintScreen (PrtScn) skickar in allt som syns på bildskärmen till Urklipp;

Alt+ PrtScn tar in aktuellt aktivt programfönster.

— OK. Men hur får vi ut (standardkommando sedan datorernas urtid är Ctrl+V) något sådant från Urklipp på Image1?

   Det rena RÅA kodKommandot som utför den uppgiften SKULLE se(r) ut så här i DELPHI(4):

 

Image1.Picture.BitMap.Assign(Clipboard);

 

Elementär flytbildsteknik — grundexempel i DELPHI4

 

VI MÅSTE EMELLERTID VARA YTTERST FÖRSIKTIGA HÄR: Urklipp har (mängder med) olika FORMAT.

   Det är det första.

   Det andra är:

— Vi ska INTE ta in urklippsbilden till Image1, utan till Image2 — flytbildsInstansen — som ska läggas PÅ Image1 när vi (här, strax) använder Ctrl+Piltangenter för att flytta omkring Importen — i olika kombinationer man andra intagna bildobjekt.

— För ändamålet måste vi då aktivera (t.ex.) en ny procedur OnKeyDown för Form1

 

 

— oftast den i särklass mest innehållsrika proceduren i hela programmet, enligt min erfarenhet. Vi passar också på att lägga till en ny Global Variabel ”ClipBoardImported” som talar om var vi är och som sedan kan användas inifrån andra rutiner för säker hantering, SAMT en extra rutin för att också RENSA (F2) Image1 och nollställa för nytt.

— Koden på KeyDown (Från Autoifyllt av DELPHI4 via F11, OnKeyDown, Ctrl+Enter som ger procedurrubriken med begin och end) ser då ut så — för resultatredovisningens del har jag för tillfället lagt in en Image1.Canvas.Draw som ritar ut InKlippet på Image1 så att vi ser att allt fungerar, vi ska senare modifiera den delen och göra den verkligt avancerad — samt en liten InfoBox

 

 

för fallet att ingen BitMap finns i Urklipp:

 

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

  if Shift=[ssCtrl] then

  begin

    case Key of

  {ClipToImage:}

      Ord('V'): begin

                  if(Clipboard.HasFormat(CF_BITMAP))then begin

  {BildMåtten för Image2&3 ges automatiskt med nedanstående:}

                    Image2.Picture.LoadFromClipBoardFormat(

                    cf_BitMap,ClipBoard.GetAsHandle(cf_Bitmap),0);

                    Image3.Picture.LoadFromClipBoardFormat(

                    cf_BitMap,ClipBoard.GetAsHandle(cf_Bitmap),0);

                    Image1.Canvas.Draw(0,0,Image2.Picture.Bitmap);

                    Label1.Caption:= 'ImageFromClip OK';

                    ClipBoardImported:= True;

                  end {endIfClipIsBitMap} else

                  begin

                    Application.MessageBox(

                   'Urklipp har ingen bitmap.'

                   ,'ClipInfo',mb_OK+mb_Iconasterisk);

                  end;{endElseIfClipHasFormat}

                end;{endCtrl+V}

    end;{endCaseCtrl+Key}

  end;{endIfCtrl}

 

  if Shift=[] then

    case Key of

      VK_F2:    begin

                  with Image1.Canvas do begin

                    Brush.Color:= clWhite;

                    FillRect(ClientRect);

                    ClipBoardImported:= False;

                    Label1.Caption:= 'Image1CLEAR';

                  end;{endWithIm1Canvas}

                end;

    end;{endCaseKeyNoShifts}

end;{endKeyDown}

 

Tangenterna Ctrl+V tar in bilden från Urklipp och ritar ut den från TopLeft på Image1;

Tangenten F2 rensar bildytan för nytt och nollställer alla flaggor (ClipImported=False).

Exempel som tagit bilddump av textavsnittet närmast ovan:

 

Resultat1:

 Efter F2:

 

 

Vi behöver aldrig oroa oss för ÄVENTYRLIGA BILDMÅTT — större eller mindre än själva programfönstret går på ett ut: Windows operativsystem sköter automatiskt om alla utanföravdelningar genom Rektangelfunktionerna som ligger till grund för bildhanteringsfunktionerna.

— Vi SKULLE (här) kunna »passa på» att redan nu lägga till en bekväm bildhanteringsfunktion:

— Tangent F5 (»uppdatera») får verkställa en automatisk fönsterStorleksAnpassning till aktuellt intagen bild, om vi så vill.

— EMELLERTID. Innan vi gör det finns en mera angelägen uppgift som innefattar samma nödvändiga programkod:

— Testa (nämligen) i vänsterexemplaret ovan att ändra fönsterstorleken: bilden raderas. Ajö. Ajö.

   Hur bevarar vi en redan intagen bild vid ändring av fönsterstorleken? Mycket viktig funktion.

 

Hela lösningen består i en ORDNAD SAMMANSTÄLLNING av samtliga de flytt- och REKTANGELPARAMETERAR som krävs i synkroniseringen mellan alla Image1-3 för att få flytbilden (Im2) att fungera: alla insättningsvärden för flytbildens TopLeft (X0,Y0) och CopyRectSource och CopyRectDestination måste finnas med.

 

Vi inför först för ändamålet Globalvariablerna CopyRektanglar (TRect) CRd(estination) och CRs(ource), samt InsättningsReferensPunkten LeftTop X0 och Y0 för flytbilder (vi kommer till den mera ingående längre fram)

 

var

  CRs,CRd: TRect;

  X0,Y0:   Integer;

FlytBildsTEKNIK:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

GlobalVariabler och Konstanter ska skrivas närmast över TypeBlocket

globala procedurer skrivs inuti TypeBlocket Unit1 och utan ”TForm1.” och med inledande TForm1. till aktuella programproceduren i Implementationsektionen = huvudblocket för alla programprocedurer; Se även Delphihjälpen på Public and private declarations —

så att vi sammantaget får, i nuvarande skede från början i Unit1:

 

unit Unit1;

{ProjectP2|8Sep2014 — Im123FromStart}

 

interface

 

uses

  Windows, Messages, SysUtils, Classes,

  Graphics, Controls, Forms, Dialogs,

  ExtCtrls, StdCtrls, ClipBrd;

 

{GlobalVARIABLES|Constants .......................................:}

const

{MomentaryCursor:}

  MC1 = 5;

var

  ClipBoardImported: Bool;

  CRs,CRd: TRect;

  X0,Y0:   Integer;

 

{GlobalVARIABLES|Constants ........................................}

type

  TForm1 = class(TForm)

    Panel1: TPanel;

...

 

Anledningen att jag (här) sätter dessa variabler som globala, inte lokala (i så fall längre ner, närmast över själva sektionen med de verkställande procedurblocken) är att jag av erfarenhet redan vet att OM — mer av regel än undantag, i programmets VÄXANDE MED TIDEN — flera Unit:s kommer med i bilden, dessa behöver använda Unit1-variablerna. Är dessa lokala går inte det.

   Vi gör nu först följande förberedande tillägg direkt vid ClipImport Ctrl+V — Föregående Image1.Canvas.Draw är nu avställd och har ingen operativ funktion längre:

 

         //Image1.Canvas.Draw(0,0,Image2.Picture.Bitmap);

         X0:=0; Y0:=0;

         CRs:=Rect(0,0,Image2.Width,Image2.Height);

         CRd:=CRs;

         Image3.BoundsRect:= Image2.BoundsRect;

         Image3.Canvas.CopyMode:= cmSrcCopy;

         Image3.Canvas.CopyRect(CRd,Image1.Canvas,CRs);

         Image1.Canvas.CopyMode:= cmSrcCopy;

         Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

 

 

Sista raden explicit ERSÄTTER föregående tillfälliga resultatritning ”Image1.Canvas.Draw(0,0,Image2.Picture.Bitmap);”. Vi kan bara bocka för = avställa den med två delstreck (DELPHI kursiverar och mörkblåfärgar allt som inte är kod)

//Image... eller inom listparenteser

{Image ...} eller mellan två stjärnor inom rundparenteser

(*Image ...*).

   Vi testar först att det utlovade fungerar:

 

 

IJÄss.

— OK. Vad är Hemligheten nudå med att få ha min bild KVAR vid ändring av fönsterstorlek?

— Vi utvidgar föregående enkla kod på FormResize

 

procedure TForm1.FormResize(Sender: TObject);

begin

  CoFormCreate;

end;{endFormResize}

Elementär flytbildsteknik — grundexempel i DELPHI4

 

till — du måste se det för att tro det: Tryck PrtScn, Starta P2, Tryck Ctrl+V, Bildskärmsdumpen kommer in TopLeft; tryck Alt+Space för att fälla ned fönsterändringsmenyn, tryck S för Ändra storlek; pila markören till HögerBotten; pila Höger Neråt;

— vartefter fönsterstorleken ökas, syns nu alltmer av den intagna hela skärmbilden; Testa också Alt+Space med X för Maximera: hela skärmdumpen syns — och tillbaka med Alt+Space och Enter för ÅterställFönsterstorleken till föregående; inte ett enda flimmer; helt perfekt rent:

 

 

 

procedure TForm1.FormResize(Sender: TObject);

var

  BR,CRsB,CRdB: Trect;

  B: TBitmap;

begin

{FÖRBEREDER intagning av Image1:}

       B:= TBitmap.Create;

       try

{BILDYTAN TBitmap:}

        BR:= BitmapRect(Image1.ClientRect,B);

{Lägger in (föregående) Image1 på Bitmap'en:}

        B.Assign(Image1.Picture.Graphic);

        B.Canvas.CopyRect(CRd,Image3.Canvas,CRs);

 

{KÖR NYA KOMPONENTPLACERINGAR:}

        CoFormCreate;

{ANPASSAR nya bildmått:}

        CRsB:=Rect(0,0,Image2.Width,Image2.Height);

        CRdB:=CRsB;

        Image1.Canvas.CopyMode := cmSrcCopy;

        Image3.Canvas.CopyRect(CRdB,Image1.Canvas,CRsB);

{Lägger tillbaka Im1BeforeResize på nya Im1:}

        Image1.Canvas.CopyRect(BR,B.Canvas,BR);

{Kopierar FlytBildsRektangeln av Im1(=Im2-ytan med Im1-bilden) till Im3:}

        Image3.Canvas.CopyRect(BR,B.Canvas,NewOR(BR,X0,Y0));

       finally

        B.Free;

       end;

{Återställer FlytBilden (Im2) på Im1:}

       //Image1.Canvas.CopyMode := cmSrcAnd;

       Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

end;{endFormResize}

 

Anledningen varför jag (här) skrivit ut CopyMode:= cmSrcCopy är bara för att (i mina allmänna DELPHIrefrenser) SÄKRA sådan kopieringsmod då programkoden i övrigt är (oerhört) välbesutten på många andra typer: Standard från start är cmSrcCopy för en TImage eller TBitMap, men ändras den för den komponenten eller instansen, kvarstår den inställningen också tills ändrad igen.

— Hur de olika sätten fungerar får vi se först när vi kan kombinera flera olika bildintag över, i eller genom varandra.

— Vi ska strax försöka komma dit.

 

med (TypeBlocket, början Unit1: global som kan nås av alla kommande Units) [Se BMR]

 

    function BitMapRect(R:TRect; var B:TBitmap):TRect;

    function NewOR(R: TRect; X,Y: integer): TRect;

 

som i ImplementationBlocket (aktuella funktioner och procedurer i Unit1) har formen (+TForm1.)

 

    {Skapar en ny, LeftTop-nollad, TRect som bildytan för

    en medsänd TBitmap:}

    function TForm1.BitMapRect(R:TRect; var B:TBitmap):TRect;

    begin

     OffsetRect(R,-R.Left,-R.Top);

     B.Width:=  R.Right;

     B.Height:= R.Bottom;

     BitmapRect:= R;

    end;

    {Skapar en ny, flyttad, TRect från en medsänd:}

    function TForm1.NewOR(R: TRect; X,Y: integer): TRect;

    begin

     OffsetRect(R,X,Y);

     NewOR:= R;

    end;

 

Se särskilt beskrivningen i BMR. Användbarheten i speciellt BitMapRect-funktionen är i min erfarenhet utomordentligt omfattande i Delphiprogrammeringen: ytterst stor användbarhet.

 

FönsterAnpassning, F5

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Anpassning F5 — programfönstrets storlek anpassas till importerad bildstorlek

 

minsta: Image1.Height = 50p:  WindowsSystem ser automatiskt till att MINST avslutningsknappen finns med.

 

 

Form1ToppListen tar up 23pixels [eg.22, men vi har här lagt till en extra till Image1-kanten av designskäl]:

Panel2 ytterligare + 2×3 pixel i rammarginalen för programfönstret

 

Nu kan vi förverkliga fönsteranpassningen till den importerade bildstorleken, om så önskemål finns.

— På FormKeyDown skriver vi då in koden (var iW,iH,x,y: Integer; tillagt på FormKeyDown):

 

 {ANPASSA EFTER IMPORT · 29Jun2011|9Sep2014:}

      VK_F5:    begin

                  if not ClipBoardImported then exit;

                  iW:= Image2.Width +2;

                  iH:= Image2.Height+2;

                  Panel1.Width:=  iW;

                  Panel1.Height:= iH;

                  x:= iW + 6 +1;

 

                  if iH > Panel2.Height

                  then

                  y:=

                  iH +

                  + Panel2.Height

                  + 23   + 6 +1

                  else

                  y:=

                  Panel2.Height

                  + 50   + 6 +1;

 

                  if x > Screen.Width  then y:=Screen.Width;

                  if y > Screen.Height then y:=Screen.Height;

                  Width:= x;

                  Height:= y;

 

                  if BoundsRect.Right > Screen.Width then

                  Left:= Left - (BoundsRect.Right  - Screen.Width);

                  if BoundsRect.Bottom > Screen.Height then

                  Top:=  Top  - (BoundsRect.Bottom - Screen.Height);

{Ta om Im2Kopieringen:}

                  ClipBoard.Assign(Image2.Picture);

                  {RENSAallt:}

                  Key:= VK_F2;

                  FormKeyDown(Self,Key,[]);

                  {TaOmImporten:}

                  Key:= Ord('V');

                  FormKeyDown(Self,Key,[ssCtrl]);

                end;{endF5}

 

Width och Height utan den sedvanliga komponentspecificeringen behöver aldrig göras om komponenten är Form1.

— Ovanstående kan skrivas med samma funktion: Form1.Width:= och Form1.Height:= .. .

 

Direkt efter ClipImport med Ctrl+V:

 

 

Efter F5:

 

Perfekt.

— Jag har här lagt till en marginal högerBotten på 1 pixel som visar Image1-bakgrunden, här vitt, och som ger lite extra relief för mörkare bildImporter högerBotten.

 

BildEXPORT, Ctrl+I

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Rutin för BildExport — Ctrl+I

Vi vill nu också DIREK förverkliga EXPORT av aktuell bildyta till Urklipp. Tillägget nedan ImageToClip på FormKeyDown [Ctrl] — vi lägger till variabler lokalt i FormKeyDown vartefter de behövs, här en B: TBitMap; och en R: TRect; :

 

var

  B: TBitMap;

  R: TRect;

  iW,iH,x,y: Integer;

begin

  if Shift=[ssCtrl] then

  begin

    case Key of

  {ImageToClip:}

      Ord('I'): begin

                  B:= TBitmap.Create;

                  try

                   R:= Image1.ClientRect;

                   BitMapRect(R,B);

                   B.Canvas.CopyMode:= cmSrcCopy;

                   B.Canvas.CopyRect(R,Image1.Canvas,R);

                   Clipboard.Assign(B);

                  finally

                   B.Free;

                  end;{endTry}

                  Label1.Caption:= 'ImageToClip|OK';

                end;{endCtrl+I}

  {ClipToImage:}

      Ord('V'): begin

                  if(Clipboard.HasFormat(CF_BITMAP))then begin

                ...

 

Vi testar resultatet genom att köra Ctrl+I från en intagen bild Ctrl+V, minska fönsterstorleken, köra Ctrl+V och sedan F5 som bevisar att ursprunget återtas: ... IJjjj .. Äss. Stämmer bra det.

   (Det händer ibland att det blir ... IJjjj .. ävlar — man missar ibland. Bara att ta paus och försöka med BättreKod).

 

PilotDISPLAY:

Indikering för intagen bildstorlek och aktuella skärmkoordinater för Peken

 

 

DatorPilotens MinimiPanel:

1. Färgen under Peken — en liten [topp]ruta som ALLTID visar vilken färgen är under bildmarkörens HotSpot

2. Pekens ImageKoordinater X;Y — vi måste ha en OnMouseMove-rutin för det

3. Bildmåtten efter import från Urklipp — vi behöver hur som helst en extra panel under programfönstrets topplist

 

Innan vi nu går vidare måste vi ha PILOTCERTIFIERANDE BASINFO — paneldata — som talar om för oss var vi är i framstegen:

0. En liten ruta som visar aktuell FÄRG under peken — mycket användbart (enkla färgval, bl.a. vid ritning)

1. En Display som visar ImportMåtten från bilden i Urklipp;

2. En allmän Peka(X;Y) som visar var pekarKrysset befinner sig på Image1, bildytan som är vårt arbetsbord.

 

 

Jag sa ju det:

— DELPHI4 i Windows7 är Kanon.

 

Form1 Project P2 ovan efter import av en enklare RGB-palett, samt insatta 3 nya paneler: Panel3 för färgvisning under Peken, Panel4 för XY-koordinaterna och Panel5 för Importens bildmått. Nöj dig inte med mindre.

— KODEN på Image1MouseMove (Image1, Object Inspector, OnMouseMove, Ctrl+Enter):

 

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,

  Y: Integer);

begin

 

                  Panel4.Caption:=

                  IntToStr(X) + ';' +

                  IntToStr(Y);

                  V:= Image1.Canvas.Pixels[X,Y];

                  Panel3.Color:= V;

  {Eliminerar marginellt flicker:}

                  Panel3.Refresh;

 

end;

 

Variabeln V: TColor Global; samt infogat på ClipToImage FormKeyDown Ctrl+V för Panel5:

 

                  Panel5.Caption:=

                  IntToStr(Image2.Width)+';'+

                  IntToStr(Image2.Height);{}

end;

Elementär flytbildsteknik — grundexempel i DELPHI4

 

och på F2 efter ClearIm1,

 

Panel5.Caption:= 'Dimensions';

 

så att inga siffror »ligger och skräpar» sedan sista Importen raderats.

 

MarkörACCELERATORER:

Inrättning av markörflyttning med piltangenter och acceleratorer (CursAcc)

 

Innan vi går vidare måste vi nu koppla ihop det (snygga) ovan med en EXAKT PekarNavigering — inte minst för ändamålet att KUNNA pricka in exakta färgmakeringar i olika tillval (färgenUnderPeken), utan mera allmänt för exakta bildarbeten med exakta passningar och positioner: det hänger på pixeln. Utan det kan vi lika gärna kasta ut datorn direkt: värdelöst SKRÄP.

— Tänk KLASSISKT:

— Vi som är uppfödda med Penna, Linjal, Passare som GRUNDBLOCK, vill ALLTID ha MINST »en fungerande ritpenna i bröstfickan»: räta linjer, enkla figurer PÅ STUBINEN.

— Vi ska återkomma till den detaljen, strax.

 

Betingelser för att realisera funktionerna MARKÖRFLYTTNING:

1. FormKeyUp måste introduceras för att nollställa den allt växande heltalsvariabeln CursAcc efter Piltangerna (KeyDown), annars kommer Peken att accelerera iväg för evigt med allt högre hastigheter och allt större steglängder.

2. En separat MarkörMove-procedur måste inrättas som styr funktionen via KeyDown-tangenterna PilUpNerVäHö.

3. En särskild procedur som uppdaterar Image1-Måtten — kopplar till Rezise:

 

  Procedure TForm1.SetSCRIm1Rect;

  begin

{GENERELLT Image1RectDimensions}

       SCRIm1.TopLeft:=Image1.ClientOrigin;

       SCRIm1.BottomRight:= Point(

       Image1.ClientOrigin.X +

       Image1.ClientWidth  + 1,

       Image1.ClientOrigin.Y +

       Image1.ClientHeight + 1 );

  end;{endSetSCRIm1Rect}

 

 

Proceduren ovan används (så småningom ju mer avancerat funktionerna flätas samman) av flera instanser i flytbildshanteringen, och gör sig därför bäst som fristående, global, procedur som kan användas av alla.

 

 

— Test visar att FormCreate ALLTID genomgår Resize-blocket, i vilket fall, och vi kan därför förlägga ett separat anrop till Image1UppdateringsRutinen ovan DIREKT i FormResize (sist: SetSCRIm1Rect;): Därmed ges Image1-dimensionerna direkt i SCRIm1-rektangeln från programstart: och sedan vidare med varje storleksändring av programfönstret;

4. Fyra separat BEKVÄMA positionerare behövs (ibland, bekvämt) för att sätta peken i endera bildhörnet; vi använder här tangenterna HOME(LeftTop), END(LeftBottom), PageUp(RightTop) och PageDown(RightBottom) för det;

 

— KODEN i DELPHI4 för att realisera ovanstående:

 

NyaGlobalaVariabler:

 

  SCRIm1:  TRect;          {Image1Rectangle}

  CursAcc: Integer;        {MarkörAcceleratorn}

 

MarkMoveProceduren, Global (TForm1. i TypeBlocket, utan TForm1. i procedurblocket) — Programexekveringen skickas hit från KeyDown om rena piltangenter utan Ctrl och Shift:

 

  {MARKÖRFLYTTNINGEN|orig.Rot1:}

  Procedure TForm1.MarkMove(Key: word);

  var

    P:   TPoint;

    X,Y,W,H: Integer;

  begin

   with Image1 do

   begin

    X:= ClientOrigin.X;

    Y:= ClientOrigin.Y;

    W:= ClientWidth;

    H:= ClientHeight;

   end;

   GetCursorPos(P);

{DIREKTREFERENS I HELSKÄRMSKOORDINATER:}

   if (PtInRect(SCRIm1,P)=True) then begin

    case Key of

         VK_RIGHT: begin if P.X+CursAcc<X+W then begin

                   P.X:= P.X + CursAcc; Inc(CursAcc);

                   end else CursAcc:= X + W - P.X - 1;

                   end;{endRight}

         VK_LEFT:  begin if P.X-CursAcc+1>X then begin

                   P.X:= P.X - CursAcc; Inc(CursAcc);

                   end else CursAcc:= P.X - X;

                   end;{endLeft}

         VK_UP:    begin if P.Y-CursAcc+1>Y then begin

                   P.Y:= P.Y - CursAcc; Inc(CursAcc);

                   end else CursAcc:= P.Y - Y;

                   end;{endUp}

         VK_DOWN:  begin if P.Y+CursAcc<Y+H then begin

                   P.Y:= P.Y + CursAcc; Inc(CursAcc);

                   end else CursAcc:= Y + H - P.Y - 1;

                   end;{endDown}

    end;{endCaseKey}

  {VERKSTÄLLER:}

    SetCursorPos(P.X,P.Y);

   end;{endIfPtInRect}

  end;{endProcedureTForm1.MarkMove}

  {NOTERING.

  1   För att markörflyttningen ska fungera måste en särskild

      rutin för bildkoordinaternas uppdatering (via SCRIm1)

      finnas på den särskilda proceduren vid OnPaint|Resize.

  2.  CursAcc måste återställas till stegning för 1 pixel

      på FormKeyUp, annars steppas allt större distanser i

      varje steg.}

Elementär flytbildsteknik — grundexempel i DELPHI4

 

FUNKTION:

 

 

När vi trycker ner en piltangent, här PilHöger, och håller tangenten nedtryckt, ackumuleras flyttstegningen successivt med tiden (värdet i CursAcc ökar), och Peken drar iväg allt snabbare över bildskärmen. När den når fönsterkanten, retarderar den (snabbt) och stannar.

— Det gör det LÄTT för oss att SNABBT hitta/nå EXAKTA positioner — med en kombination av MusRörelse och Piltangenter.

— Koden på KeyDown för HomeEndPageUpDown, Peken i endera bildhörnet:

 

      VK_PRIOR: begin {PageUp | HögerÖvre:}

                  SetCursorPos(Image1.ClientOrigin.X+Image1.ClientWidth-1,

                  Image1.ClientOrigin.Y);

                  exit;

                end;

      VK_NEXT:  begin {PageDown | HögerNedre:}

                  SetCursorPos(Image1.ClientOrigin.X+Image1.ClientWidth-1,

                  Image1.ClientOrigin.Y+Image1.ClientHeight-1);

                  exit;

                end;

      VK_END:   begin {PageDown | VänsterNedre:}

                  SetCursorPos(Image1.ClientOrigin.X,

                  Image1.ClientOrigin.Y+Image1.ClientHeight-1);

                  exit;

                end;

      VK_HOME:  begin {PageDown | VänsterÖvre:}

                  SetCursorPos(Image1.ClientOrigin.X,

                  Image1.ClientOrigin.Y);

                  exit;

                end;

DelResultat:

Ctrl+V       Importera Bild FrånUrklipp

Ctrl+I        Exportera HelaBildytan TillUrklipp

F2            Rensa Bildytan

F5            Anpassa programfönstret till ImporteradBild

 

Piltangent  Fri, accelererad markörflyttning, eller exakt enskild pixelstegning

Home        Peken LeftTop

End           Peken LeftDown

PageUp      Peken RightTop

PageDown  Peken RightDown

 

 

Jag har också lagt in VK_HOME-kommandot på FormCreate så att programförnstret alltid startar upp med peken där.

(Detta grepp är mindre lämpligt med hänsyn till klickutflykter för andra programfönster på bildskärmen. Jag har t.v. bockat av den delen då vi i vilket fall bara behöver trycka HOME för att få se peken LeftTop med aktivt P2).

— För Programnamnlisten, se Caption.

 

Flytbilden, Tabell

Målet nått 9Sep2014 — [det tog 5 dagar att sammanställa]

 

Flytbilden

 

Nu är vi klara för att förverkliga drömmen:

— Importerade UrklippsBilder ska kunna flyttas på, över, i och genom bildytan Im1 efter de olika ELEMENTÄRA »transportsätten».

— Vi kallar här ett sådan flyttbart bildobjekt för en flytbild.

   Vi studerar det.

 

 

Ctrl+Pilar      flyttar UrklippsImporter Ctrl+V över bildytan i FLYTSTEG om Ctrl+NumPad|1 2 3 5 9 antal pixels 1 5 20 halvsida(höjd) helsida(höjd);

— Från programstart är flytintervallet inställt på 20 pixels (min erfarenhet, bästa kompromissen för alla fall i grov flyttning).

— Intaget ovan från hel skärmbild (PrtScn), textområdet har flyttats så att bara text syns, sedan en separat ClipExport från annat program med RGB-paletten, denna in via Ctrl+V sedan flyttad med Ctrl+PilHöger i CopyMode standard cmSrcCopy: Flytbildens hela bildrektangel ligger täckande underlaget. Vi ska senare se hur också samtliga övriga flytbildsSÄTT kan förverkligas via DELPHI4-KOD. Den delen kräver att vi måste genomgå alla flytbildssätten i ett gemensamt block (s.k. FLYTBILDSFLAGGOR: dessa styr tillvalen och kräver extra PilotPaneler i DISPLAY för vår Info).

— Genom speciell kodning [vidare nedan: MarkMove(0)], uppdateras här även färgrutan nedrevänster som visar färgen under peken då flytbildens färger passerar i flytten — utan någon MouseMove (»It’sMacig»).

 

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Koden i DELPHI4 som slutligen förverkligar flytbilden som ovan ser ut så här — KeyDown i Ctrl-blocket med tillagd globalvariabel

ArrowStep: Integer; 20pixels från programstart [ASMM ännu bara = 1; –1 ger omvända flytriktningar]:

 

      VK_LEFT,VK_UP,VK_RIGHT,VK_DOWN:

                begin

{ASMM·piltangentsvändare|ArrowSwapperMoveMore|, ArrowStep·1,5,20 pixels:}

{PREPARE SourceDestinationRECTANGELS:}

                  CRs:=Bounds(0,0,Image2.Width,Image2.Height);

                  CRd:=Bounds(X0,Y0,Image2.Width,Image2.Height);

{ÅTERSTÄLLER Image1|Lägger ut föregående InKopia i Im3:}

                  Image1.Canvas.CopyMode:= cmSrcCopy;

                  {cmSrcAnd här = avtryck efter varje flyttning.}

                  Image1.Canvas.CopyRect(CRd,Image3.Canvas,CRs);

{VERKSTÄLL FLYTSTEG:}

                  case Key of

                   VK_LEFT:   X0:=X0-ArrowStep*ASMM;

                   VK_RIGHT:  X0:=X0+ArrowStep*ASMM;

                   VK_UP:     Y0:=Y0-ArrowStep*ASMM;

                   VK_DOWN:   Y0:=Y0+ArrowStep*ASMM;

                  end;{endCaseKey}

{UPPDATERAR Destinationskoordinaterna:}

                  CRd:=Bounds(X0,Y0,Image2.Width,Image2.Height);

{SPARAR UNDAN originalytan, CRd/s omvända:}

                  Image3.Canvas.CopyRect(CRs,Image1.Canvas,CRd);

{FLYTBILDEN|Im2 LÄGGS PÅ NY PLATS:}

                  Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

                  MarkMove(0);

      {MarkMove(0)|AktiverarMouseMove via en DoNothing.

       Därmed uppdateras Färgrutan, samt övriga data

       som (ev.) finns på Im1 i anslutning till olika

       PekRörelser/Flytbilder.}

                  exit;

                end;{endIf Ctrl+Arrows}

 

Förstummande galant.

— GRUNDJOBBET till allt det ovan — nu använt under mer än 15 år — gjordes (faktiskt) på 16 bitars Delphi1: värdens underbaraste MasterProgram (slutet av 1990-talet, Windows 95), just på grund av dess NÄRA anslutning till MIKROPROCESSORN: 16-bitars programmering som tvingade mig att lösa problemen med sömlösa övergångar i BitMaps med gränsblock om 65535Byte — och som ibland förorsakade kommersiella WindowsKrascher för Microsofts egna programvaror (MsWorks Utskrifter) [som bara bevisade hur marknaden SLARVAR med KOD (Microsoft kan ännu i denna dag inte lösa ELEMENTÄRA programuppgifter: jämför SökMotorerna på datorns hårddisk: rena katastrofen i Microsoft)].

 

RitaMusHöger

 

Enkelt ritverktyg tillagt. MusHögerNer. Alltid tillgängligt. MED piltangenter som Kan Accelerera. Rakt.

 

 

Följande tillägg på Image1MouseMove ger ovanstående enkla MANUELLA MED ACCELERERANDE PILTANGENTESFUNKTIONEN INKLUDERAD pennritningsfunktion: Höger musknapp ner

— när som helst. AnyTimeYouWant:

 

var

  C1: Integer;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,

  Y: Integer);

begin

...

                  {DRAW SIMPLE LINES|MouseRight:}

                  if GetKeyState(VK_RBUTTON)<=-127 then begin

                   with Image1.Canvas do begin

                     if C1=1 then

                     begin

                     Pen.Style:= psSolid;

                     Pen.Color:= 0;

                     Pen.Width:= 1;

                     LineTo(X,Y);

                     end else begin

                     MoveTo(X,Y);

                     C1:= 1;

                     exit;

                     end;

                   end;{endWithIm1Canvas}

                  end{endIfMouseRight}else

                  C1:= 0;

 

Separata variabler (var) som deklareras omedelbart före en procedur ses av (bara) den proceduren (och alla efterföljande) som en »global» variabel. Sätter vi C1 som en variabel i själva proceduren, fungerar inte koden därför att värdet på C1 då förloras med programmets exekverande procedurutgång [DELPHI4 nollställer i allmänhet alla procedurutanförstående variabler från programstart — men man bör inte ta det för säkert. Säkert är dock att alla reguljärt globalt deklarerade variabler ALLTID nollas från programstart av DELPHI4. Se vidare i DelphiHjälpen (Declaring variables)].

 

MoveMore:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

 

Ctrl+M      Reverserar flytriktningen via piltangenterna Move|More:

— M-panelen ovan (Panel6) har lagts till med koden nedan på Ctrl-FormKeyDown-blocket

 

  {ArrowsSwapMoveMore|ASMM:}

      Ord('M'): begin

                  ASMM:= - ASMM;

                  if ASMM=1 then

                  Panel6.BevelOuter:= bvRaised else

                  Panel6.BevelOuter:= bvLowered;

                end;

 

ASMM=1 från programstart: flyttning av importbilder från urklipp lämpat för bilder mindre än fönsterstorleken: MOVE.

— Större bilder vill man ser mer av MED SAMMA TANGENT som då uppför sig rebelliskt »åt andra hållet».

— Ett tryck på Ctrl+M ändrar den situation och ger det man vill ha: showmeMORE.

— Notera att ASMM-värdet kopplar (se längre upp) bildflyttningens X0|Y0-ArrowStep*ASMM.

 

FleraÖppnaFönster, MultiWin — 10Sep2014

 

Flera öppna programfönster

 

Vi kan öppna i princip hur många programfönster som helst av typen som skapas av DELPHI4-Windowsprogram. Enda problemet är att vi måste utforma något SÄTT att dels skilja dem åt — här genom numrering — och dels kunna identifiera vad som gäller speciellt vid programavslutning, om uppgiften gäller att spara undan data som man sedan vill kunna återuppta vid en senare programstart. Här är DelphiKoden för det:

 

 

 

 

Öppnas n stycken fönster och ett i högen klickas av, kommer nästa programfönster som startas upp att ta platsen för det fönster som klickats av med lägsta ordningsnummer: inga tomrum tillåts.

 

 

 

Koden nedan (testat speciellt på det krångliga vildsinta operativsystemet Windows Vista — tenderar att gruppera program med samma ursprung)

har

[med nuvarande 10Sep2014 viss reservation för WindowsVista: min Vistadator har slutat fungera efter 7 år, och den delen kan här inte testas längre med den numera modifierade Delphikoden nedan

[avställt {(NumWin=0)and} i if{(NumWin=0)and}(MultiWin=False)then]

testats upp till Windows 7 och har visat sig fungera galant:

 

1. Vi behöver först en särskild procedur som skannar av vilka öppna Windowsprogram som finns (GlobalProceduren deklareras som vanligt i TypeBlocket överst i Unit1 som nedan men utan TForm1.), NumWin: Integer; är global (deklareras efter UsesBlocket, Unit1):

Procedure TForm1.CheckMultiWin;

var

  T:       TStringList;

  S:       string;

  N,Z,X,C: Integer;

  PC:      PChar;

begin

       T:= TStringList.Create; T.Sorted:= True;

       NumWin:= 0; C:= 0; Z:= 1;

       PC:= StrAlloc(51);

       X:= GetWindow(Form1.Handle,GW_HwndFirst);

       try

{SKANNA AV ALLA ÖPPNA WINDOWSFÖNSTER:}

         repeat

           Inc(C); X:= GetWindow(X,GW_HwndNext);

           GetWindowText(X,PC,50);

           S:= StrPas(PC);

{INGEN FRÅN FÖRSTA:}

           if Pos(' P2 | Form1 ',S)>0 then

           begin

            Inc(NumWin);

           {Caption:= '  P2 | Form1 |  — ' }

           {           123456789012345678 :}

            Delete(S,1,18);

            T.Add(S);

{REDAN BEFINTLIGA FÖNSTERNUMMER N-1:}

            N:= StrToInt(S) +1;

{FYLLER PÅ MED +1 EFTER SENAST HÖGSTA VÄRDE:}

            if N>Z then Z:= N;

           end;

         until(X=0)or(C>=1000);

         {ClipBoard.AsText:= IntToStr(C)|Test visar C=242 vid X=0.}

{SÄKRA FÖRST RAKA NUMRERINGEN|Om inget annat gäller:}

         NumWin:= Z;

{KOLLA OM LUCKOR FINNS I NUMRERINGEN|OmSå, ta närmast lägsta lediga:}

         for N:= 0 to T.Count-1 do

         if StrToInt(T[N])<> N+1 then

         begin

           NumWin:= N+1;

           Break;

         end;

       finally

         StrDispose(PC);

         T.Free;

       end;

       if NumWin-1>1 then MultiWin:= True else MultiWin:=False;

       {Numreringen för sista fönstret kommer att sluta på NumWin=2:

       — Första sökningen på GetWindowText(X,PC,50) ger noll eftersom första

       programfönstret ännu inte finns. Då sedan (sista) programmet avslutas,

       finns redan en förekomst, och som räknas upp internt i ifPos-blocket.}

end;{endCheckMultiWin}

 

 

På FormCreate ersätter vi sedan föregående enkla ”Caption:= ' P2 | Form1” med det mera avancerade

 

 

{SEPARATA FÖNSTERNAMN FÖR FLERA ÖPPNADE PROGRAMFÖNSTER:}

       CheckMultiWin;

       Caption:= '  P2 | Form1 |  — ' + IntToStr(NumWin);

{DELPHI4Test2014|4Nov2013|10Sep2014|P2.}

{WindowsVista KAN försöka stänga flera (9+) öppna fönster som GRUPP — vilket resulterar i FEL:

CannotCreate DATA.txt-file på FormClose: Bara ETT programfönster åt gången får stängas:

   FÖRKLARING:

   — Om var och en av nst öppnade P2 försöker spara data vid Close för åtkomst vid nästa öppning,

   är det givet att »systemet pajar».

   LÖSNING:|4Nov2013 — se tillägget MultiWin-flaggan, FormClose. TestatOK till 12st. Hela gruppen stänger utan protest.}

{Forts. FormClose.}

       Left:= (Screen.Width -  Width)  div 2 + (NumWin-1)*20;

       Top:=  (Screen.Height - Height) div 2 + (NumWin-1)*20;

 

tillsammans med en GlobalVariabel MultiWin: Bool;.

— Vi lägger sedan till en FormClose via Object Inspector Form1, F11, OnClose, Ctrl+Enter: Mellan begin-end skriver vi in, samt lägger till variabler:

 

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

var

  S:   string;

  TS:  TStringList;

begin

   exit;

{REKOMMENDERAT FÖR DEN SOM VILL FORTSÄTTA PROGRAMUPPGIFTEN SJÄLV:}

   {Använd följande rutinblock — ge alternativt namn — om uppgiften

   är att spara undan programdata som ska användas nästa gång programmet

   startar upp — typ använda färger eller värden eller text eller vad

   det kan vara:}

{SEPARAT PROCEDUR — används på FormCreate och FormClose:}

       CheckMultiWin;

{Här exekveras TestVillkoret:}

{Se MultiWin på FormCreate|Endast sista CloseFönstret berörs:}

       if(MultiWin=False)then

       begin

{SaveDATA:}

         TS:= TStringList.Create;

         try

           SetCurrentDirectory('C:\');

           S:= ExtractFilePath(Application.ExeName);

           TS.Add('Data1');

           TS.Add('Data2');

           TS.Add('Data3');

         finally

           TS.SaveToFile(S+'DATA.txt');

           TS.Free;

         end;

       end;{endIfOnlyOneLastOpenProgramWindow}

end;{endFormClose}

 

Modifiera, utvidga eller ändra TS.Add-posterna efter egna önskemål.

— Bockas exit;-raden av (//exit; eller {exit;}) kommer en ny fil DTA.txt att läggas in i projektkatalogen med raderna.

 

Data1

Data2

Data3

 

Flytsätten, MoveCOPY och MoveAND

Elementär flytbildsteknik — grundexempel i DELPHI4

 

 

Flytbildens olika transportsätt över bildytan

 

— Ovanstående resultat för flytbilden motsvarar MoveCOPY: flytbildens totala UrklippsRektangel flyttas över den underliggande bildytan som en enskild fast yta.

— Vi inför nu först anpassad DELPHIkod för att också få den andra ELEMENTÄRA flytbildsmaken med:

— MoveAND: flytbildens pixels AND-as tillsammans med den underliggande bildens pixels:

 

1. Vi inför först två nya paneler, Panel7(C) och Panel 8(W)

 

 

C:     upphöjd, Indikerar MoveCOPY = fristående flytbildsobjekt med separata pixelkanaler

        nedsänkt, Indikerar MoveAND = kombinerar pixels hos underlag OCH flytbild

        tangent C togglar=växlar mellan dessa — vi återkommer till W (SkipWhite)

 

2. Vi anpassar kod med tre nya globala variabler

 

  CopMod:               TCopyMode;

  FleetState,SkipWhite: Integer;

 

och en särskild global procedur för uppdatering UpdateMergeFlags,

 

Procedure TForm1.UpdateMergeFlags;

begin

  if FleetState=0 then

{MoveAND:}

  Panel7.BevelOuter:= bvLowered else

{MoveCOPY:}

  Panel7.BevelOuter:= bvRaised;

  if SkipWhite=-1 then

  Panel8.BevelOuter:= bvRaised else

  Panel8.BevelOuter:= bvLowered;

{Om MoveANDgäller har SkipWhite ingen funktion:}

  if FleetState=0 then

  Panel8.BevelOuter:= bvNone;

{}

end;

 

med motsvarande tangentkommandon på FormKeyDown, blocket utan skifttangenter, CaseKeyBlocket,

 

 {MoveCOPY|AND|10Sep2014:}

      Ord('C'): begin

                  if FleetState=0 then

 {MoveCOPY:}      FleetState:= 1 else

 {MoveAND:}       FleetState:= 0;

                  UpdateMergeFlags;

                end;{endC|A}

 {SkipWHITE|10Sep2014:}

      Ord('W'): begin

                  SkipWhite:= -SkipWhite;

                  UpdateMergeFlags;

                  {ImmediateKeyResponse:}

                  {I:=ArrowStep;

                  ArrowStep:=0;

                  W:= VK_DOWN;

                  FormKeyDown(Self,W,[ssCtrl]);

                  ArrowStep:=I;{}

                end;

 

med motsvarande StartVärden på FormCreate,

 

{MoveCOPY|TakeWhite:}

       FleetState:= 1;

       SkipWhite:=  1;

 

och VERSKTÄLLANDE på BildFlyttningen, FormKeyDown, här de nya posterna markerade med orange och gult,

 

      VK_LEFT,VK_UP,VK_RIGHT,VK_DOWN:

                begin

                  if FleetState=0 then CopMod:=cmSrcAnd else CopMod:=cmSrcCopy;

{ASMM·piltangentsvändare|ArrowSwapperMoveMore|, ArrowStep·1,5,20 pixels:}

{PREPARE SourceDestinationRECTANGELS:}

                  CRs:=Bounds(0,0,Image2.Width,Image2.Height);

                  CRd:=Bounds(X0,Y0,Image2.Width,Image2.Height);

{ÅTERSTÄLLER Image1|Lägger ut föregående InKopia i Im3:}

                  Image1.Canvas.CopyMode := cmSrcCopy;

                  {cmSrcAnd här = avtryck efter varje flyttning.}

                  Image1.Canvas.CopyRect(CRd,Image3.Canvas,CRs);

{VERKSTÄLL FLYTSTEG:}

                  case Key of

                   VK_LEFT:   X0:=X0-ArrowStep*ASMM;

                   VK_RIGHT:  X0:=X0+ArrowStep*ASMM;

                   VK_UP:     Y0:=Y0-ArrowStep*ASMM;

                   VK_DOWN:   Y0:=Y0+ArrowStep*ASMM;

                  end;{endCaseKey}

{UPPDATERAR Destinationskoordinaterna:}

                  CRd:=Bounds(X0,Y0,Image2.Width,Image2.Height);

{SPARAR UNDAN originalytan, CRd/s omvända:}

                  Image3.Canvas.CopyRect(CRs,Image1.Canvas,CRd);

{FLYTBILDEN|Im2 LÄGGS PÅ NY PLATS:}

                  Image1.Canvas.CopyMode:= CopMod;

                  Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

                  MarkMove(0);

      {MarkMove(0)|AktiverarMouseMove via en DoNothing.

       Därmed uppdateras Färgrutan, samt övriga data

       som (ev.) finns på Im1 i anslutning till olika

       PekRörelser/Flytbilder.}

                  exit;

                end;{endIf Ctrl+Arrows}

 

med resultatet (skärmdump på texten ovan, sedan separat intag av RGB-palett och Tangent C som växlar till MoveAND):

 

 

Resultatet talar för sig självt: MoveAND låter oss se underlaget genom flytbildsobjektet i kombination med flytbildens pixels.

Trycker vi C igen återgår flytsättet till MoveCOPY:

 

 

 

Det finns (nu) EGENTLIGEN bara ett flytsätt kvar att beskriva — frånsett möjligheten att flytta objekten TONANDE genom varandra, vi kommer strax dit också: MoveMedSkipWHITE.

— En del (»elementära») ritprogram (Paint, Windows 7) kallar det flytsättet för »transparent», då det i själva verket »bara» betyder SKIPPA BAKGRUNDSFÄRGEN, mestadels vitt.

— När vi kopierar bildobjekt sker det ALLTYID via en omgivande begränsningsrektangel. Men den bildytan innehåller inte alltid heltäckande färger. Ofta använder vi stora bildytor med bara några få färger, typ cirklar, grafiska element generellt, som vi vill kunna passa in enbart på kurvformens figur. Det sättet kallas här generellt flytta med SkipWhite (eller annan bakgrundsfärg).

 

Omgående tangentrespons

 

För att få OMEDELBAR TANGENTRESPONS vid tryckning på tangent C — växlingen till nytt MoveSÄTT verkställs och syns omgående UTAN FLYTT — formulerar vi ett INTERNT MAKRO på UpdateMergeFlags sist (variablerna I: Integer; W: Word;):

 

  I:=ArrowStep;

  ArrowStep:=0;

  W:= VK_DOWN;

  FormKeyDown(Self,W,[ssCtrl]);

  ArrowStep:=I;

Förklaring:

— FÖRST spar vi undan vad aktuellt som finns i ArrowStep i I;

— Sedan simulerar vi en flytbildsflyttning via tangenterna Ctrl+PilNer (vilkensom) och vilken rutin finns på FormKeyDown:

— Genom att vi påtvingar ArrowStep=0 sker ingen bildflyttning, bara en kopiering i repris, men nu i aktuell form.

— Sist lämnar vi tillbaka lånet. Perfekt funktion.

 

 

HELP

Vänsterklick på den här introbilden öppnar en htm-fil (i datorns förinställda webbläsare) som beskriver programinnehållet, om ej redan bekant.

Ta bort den här introbilden genom att trycka på valfri tangent.:

 

— JäMFÖR MicrosoftSWEDEN:

DU TAR BORT DEN HÄR BILDEN GENOM ATT DU SÄTTER IGÅNG MED ATT BÖRJA TRYCKA PÅ VALFRI TANGENT.

— DU gör. DU tar. Och sen när DU har gjort det, KAN DU ta och göra det. Sen kan du göra det där andra också.

— Imperialismens Slavparadis på Jorden.

— MicrosoftSWEDEN (maktkåta översättarproffs) har möjligen aldrig reflekterat över att engelskans YOU i allmänhet i beskrivningar betyder det generaliserade MAN gör det och det, YOU do that and that, inte DU gör det och det. Det senare är OFÖRSKÄMT. Byt företag nu. Snälla.

— OM MAN INTE DIREKT har avancerat till FÖRSTÅENDE FÖRKLARANDE GUD, håller man sig städat och snyggt på nivån RekommenderaT, enligt erfarenhet: MAN kan göra si och så, och om det gäller att REALISERA det och det: man trycker först på den och sedan den, sedan kan man fortsätta ... . DU är reserverat för PERSONEN, det personliga valet, och det har ingen SOM INTE ÄR INBJUDEN med att göra.

 

 

Elementär flytbildsteknik — grundexempel i DELPHI4

 

MoveSkipWHITE

— Dramatiskt:

— Ett AccessViolationFEL envisades tills nyligen att återkomma — till synes kaotiskt — på MoveSkipWHITE-proceduren.

— Upprepade observationer visade strax:

— AcVi-avbrottet uppkommer om, och endast då, intagsbilden är större än ELLER LIKA MED bildskärmsbilden (Screen.Width resp. Screen.Height).

— När en extra  marginal på minus en pixel lades till i ScanLine-blocket, upphörde felet.

— Funktionen MoveSkipWhite har nyligen testats med intag av STORA bilder (från digitalkamera) och fungerar utan avbrott.

— Notera dock att STORA bilder med MoveSkipWhite tar MÄRKBART lång tid: flytbilden kräver för varje ny position runt 1/2 sekund för att uppdateras.

 

BaraFörVITT

I föregående kod

 

{FLYTBILDEN|Im2 LÄGGS PÅ NY PLATS:}

                  Image1.Canvas.CopyMode:= CopMod;

                  Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

 

 

finns bara en enda rad vi behöver koncentrera oss på för att förverkliga MoveSkipWHITE: den sista ovan.

   Hur görs det?

   Vi ska utforma en separat BitMap (B), lägga flytbilden Im2 på denna, tilldela B egenskapen Transparent=True, anställa vilken TransparentFärgen ska vara, och sedan helt enkelt använda Image1.Canvas.Draw(X0,Y0,B) direkt istället för ovanstående CopyRect:

 

if{(CopMod=cmSrcCopy)and}(SkipWhite=-1)then begin

B:= TBitMap.Create;

try

  B.Assign(Image2.Picture);

  B.Transparent:= True;

  B.TransparentColor:= clWhite;

  Image1.Canvas.Draw(X0,Y0,B);

finally

  B.Free;

end;

end else

Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

 

 

Med CopMod-parentesen:

BARA då MoveCOPY gäller får SkipWHITE mening:

— I flytläge MoveAND har SkipWHITE ingen innebörd — om vi ska vara konsekventa mot AND.

Utan CopMod-parentesen:

SkipWHITE ignorerar MoveAND:

— I flytläge överrider SkipWHITE tillfälligt ett MoveAND med MoveCOPY.

— Detta senare alternativ är EGENTLIGEN att föredra [ofta förekommande rutiner, man slipper trycka på två tangenter för att få SkipWhite från ett MoveAND] då SkipWHITE-funktionen (min erfarenhet) är den oftast förekommande framför MoveAND.

 

 

Vi sätter bara in koden ovan omedelbart efter föregående

                  Image1.Canvas.CopyMode:= CopMod;

— B finns redan deklarerad från tidigare i FormKeyDown:

 

 

MoveCOPY ovan: Hela intagsrektangeln med den vita ytan täcker för underlaget.

— Vi trycker nu på W = SkipWhite:

 

FörGrund¦BakGrund, FärgDisplay

 

Jag har här passat på att lägga till 2 ytterligare bottenpanelerVänster:

— Panel9(störreVita=Bakgrundsfärg) och Panel10(lillaSvartaMitten=Förgrundsfärg):

— Tangenterna BN sätter Bakgrundsfärg och FörgruNdsfärg respektive som finns just då under Peken.

— DelphiKODen är på FormkeyDown, caseKeyBlocket, variabeln D: TPoint; tillagd lokalt i FormKeyDown och globalerna fgCol,bgCol: TColor; längst upp i Unit efter UsesBlocket:

 

{SÄTT FÄRG — bAKGRUND FÖRGRUnD;}

{NoShift|BakgrundsFärg|B|Förgrund|N:}

      Ord('B'),Ord('N'):

                begin

                  GetCursorPos(D);

                  D:= Image1.ScreenToClient(D);

                  if not PtInRect(Image1.ClientRect,D) then exit;

                  case Key of

      Ord('B'):   bgCol:= Image1.Canvas.Pixels[D.X,D.Y];

      Ord('N'):   fgCol:= Image1.Canvas.Pixels[D.X,D.Y];

                  end;

                  Panel9.Color:= bgCol;

                  Panel10.Color:= fgCol;

                  UpdateMergeFlags;

                end;{endBN}

 

Voila.

— Ctrl+Pilar får nu flytbilden att åka över bildytan med den omgivande vita intagsrektangeln eliminerad: bara BildFiguren framträder.

 

BGOF, Begin

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Komplikationer i DELPHI(4) — BitMapTransparensen »Jävlas»

— Men håll ut: Befrielsen är nära.

 

SkipWHITE blocket ovan fungerar utmärkt med ”B.TransparentColor:= clWhite;”. Försöker man, emellertid, med andra bakgrundsfärger uppkommer (stundtals) rena snurrren: DELPHI4 förvandlas (ibland, ibland inte, på sätt som verkar helt oförutsägbara) till en vildhäst med fruktansvärda krafter. Med olika kodförsök ser det ibland ut som att en lösning är i antågande. Just närman TROR att man kan pusta ut, raseras snabbt hela arbetet till i stort sett ett osynligt dammoln, bara med en enda verkställande tangenttryckning: inget händer. Inte ett liv.

   Test med typen

 

with Image2.Picture.BitMap do begin

PixelFormat:= pf32Bit;

Transparent:= True;

TransparentColor:= bgCol;

Image1.Canvas.Draw(X0,Y0,Image2.Picture.BitMap);

end;{notOK|IngetHänder}

 

kan köras utan några som helst problem. Men inget händer: Den valda bakgrundsfärgen försvinner inte när man trycker W — om den inte är VIT förstås. Då går det. Inte annars. Flytbilden svarar helt enkelt inte på ovanstående allmänna transparenskommando.

   Inte heller den till synes mera preciserade typen

 

if{(CopMod=cmSrcCopy)and}(SkipWhite=-1)then begin

B:= TBitMap.Create;

  iW:= Image2.Width;

  iH:= Image2.Height;

R:= Rect(0,0,iW,iH);

try

  Image2.Picture.BitMap.PixelFormat:= pf32bit;

  B.PixelFormat:= pf32bit;

  BitMapRect(R,B);

  with B.Canvas do begin

   Brush.Color:= bgCol;

   FillRect(R);

  end;

  B.Assign(Image2.Picture);

  B.Transparent:= True;

  B.TransparentColor:= bgCol;

  Image1.Canvas.Draw(X0,Y0,B);

finally

  B.Free;

end;

end else

Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

 

visar livstecken, utom på vitt.

— Vi (min erfarenhet) vet redan i DELPHI4 att en TBitMap INTE svarar på typ Transparent=True om INGEN OPERATION ÄNNU HAR UTFÖRTS PÅ bildens minnesyta. Heldött. Går inte.

— Ovanstående försök innefattar emellertid just ett sådant klargörande: ytfyllnad med färg, samt särskild rutin för bildmått, säkerställer ATT TBitMap:en ska fungera.

   Likväl. Intet. Sten dött i Berget.

BakgrundsOberoendeFlytbild, BGOFbegin

Allafärger

Lösningen

Lösningen är av typen radikal, vi tar hela blocket för exakt jämförelse:

 

if{(CopMod=cmSrcCopy)and}(SkipWhite=-1)then

begin

B:= TBitMap.Create;

  iW:= Image2.Width;

  iH:= Image2.Height;

  zW:=0; zH:=0;

  if iW >= Screen.Width  then zW:=1;

  if iH >= Screen.Height then zH:=1;

R:= Rect(0,0,iW,iH);

try

  Image2.Picture.BitMap.PixelFormat:= pf32bit;

  B.PixelFormat:= pf32bit;

  B.Assign(Image2.Picture);

  B.Transparent:= True;

  B.TransparentColor:= bgCol;

  B.Canvas.Brush.Color:= bgCol;

  B.Canvas.FillRect(R);

 

  bR:= GetRValue(bgCol);

  bG:= GetGValue(bgCol);

  bB:= GetBValue(bgCol);

 

  for y:= 0 to iH-1-zH do begin

    Pa:= Image2.Picture.BitMap.ScanLine[y];

    Pb:= B.ScanLine[y];

    for x:= 0 to iW-1-zW do begin

  {IGNOREbgCol:}

     if (bR=Pa[4*x+2])and(bG=Pa[4*x+1])and(bB=Pa[4*x+0])then Continue;

  {FörÖverALLT från Im2 till B utom bgCol:}

     Pb[4*x+2]:= Pa[4*x+2];

     Pb[4*x+1]:= Pa[4*x+1];

     Pb[4*x+0]:= Pa[4*x+0];

    end;{endForX}

  end;{endForY}

 

  Image1.Canvas.Draw(X0,Y0,B);

finally

  B.Free;

end;

end else

Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

 

20Sep2014: Utan tillägget med zW och zH uppkommer ett AccessViolation = funktionen kapas = avbryts då och endast då intagsbilden är större än eller lika med bildskärmsbilden.

 

B.PixelFormat kan undvaras utan bieffekter, enligt test:

— Däremot om PixelFormat för Image2.PictureBitMap bockas för, uppkommer vid första nedtryckningen tangent W TOM FLYTBILD: den bara försvinner, och återkommer sedan utan vidare avbrott med bildflytten via Ctrl+Pilar.

ScanLineOperationer i allmänhet tillhör kategorin maximalt snabbgående, även för STORA bilder om matematiken i ScanBlocken inte är allt för omfattande: i 3D- och animeringsprogram är den just det — enorma ServerLandskap krävs, miljondatorer, för att bearbeta varje bildpixels slutdata. Här är vi mera blygsamma.

 

— PASSA HÄR GÄRNA PÅ att KOLLA HUR DELPHI4 visar information i typen Komplicerat programfel genom följande ändring i koden ovan:

 

— Ersätt iH i forYloopen med iW och iW med iH i forXloopen:

— Ctrl+F9 sker utan protester, P2 startar som vanligt med F9. Men, sedan vi importerat något (Ctrl+V) och trycker W i det startade programmet, sker avbrott med ett meddelande över nära hela bildskärmen:

 

Error

Project P2.exe raised exception class EAccessViolation with message 'Access violation at address 004428BB in module 'P2.exe.' Read of address 01E8F832'. Process stopped. Use Step or Run to continue.”.

 

— Tryck OK och sedan Ctrl+F2 för att avsluta P2 och återgå till Kodfönstret.

Ovanstående typFELmeddelande visar sig UTESLUTANDE ALLTID när man missar kod i samband med TBitMaps-rutiner eller gör slarvfel, som i ovanstående medvetet iscensatta exempel — ALLTID i samband med minneshanteringsrutiner. Ytterst svåra »kompileringsfel» att hitta och åtgärda om man som här INTE REDAN VET HUVUDORSAKEN. Ha det gärna i bakhuvudet för vidare. Fel gör vi alla, förr eller senare. (Ju grundligare, desto bättre, förutsatt VILJA att komma igen).

BakgrundsOberoende flytbild, exempel

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Galant. Befriande. Grymt snabbt — förutsatt intagna bilder är mindre än bildskärmen: ju mindre desto snabbare. Test på Intagna bilder från digitalkamera (flera gånger större än en 23 tums bildskärm) visar att MoveSkipWHITE kräver runt 1/2 sekund för att uppdatera flytbilden. Perfekt funktion.

 

Verifierande test

 

Nedan Vänster: TestBild. Mitten: Efter Import Ctrl+V till P2 och Fönsteranpassning F5. Höger: Efter ytterligare Ctrl+V — vi använder en kopia av underlaget som flytbild och lämplig passning för kontroll:

— MoveCOPY med IncludeWHITE som standard för att se intagsrektanglarna i flytbilderna från start:

 

 

Nedan Vänster: Efter W=SkipWHITE. Mitten: Efter B=bgCol med Peken över gråytan. Höger: Efter B=bgCol med Peken över svartytan.

 

 

Perfekt. Exakt som vi ville ha det.

— Nu framträder även vit text (som skapats på given homogen färgyta) i flytbilderna och som obehindrat kan flyttas över till andra godtyckliga bildytor, synligt(med bakgrunden) eller osynligt(utan bakgrunden), tangent W växlar.

— Observera här (återigen, för den som bara ser den här presentationen): alla ändringar i flytbildssätten framträder omedelbart visuellt med en enda enkel tangentnedtryckning: W C B N. Inget musklickande. Inget armvevande över halva bildskärmen för att hitta. Inget letande efter menyer och ToolBars. Lugnt. Ingen stress.

 

 

20Okt2014 — Mera TransparensenJävlasProblem:

— Men LÖSNINGEN ovan (BGOF) grusades snart med följande upptäckt:

— Perfekt funktion — tills plötsligt (och turligt för upptäcktens del) ett bildmått 388;256 kom med i bilden (även 2·388×256 ger AcVi-avbrott):

— AccessViolationAVBROTT inträder när SkipWhite-funktionen aktiveras.

  LÖSNINGEn — vi gör oss oberoende sv ScanLine-funktionen genom att läsa direkt från och skriva direkt till bildens dataminne:

 

{ENDA FUNGERANDE HELHETSLÖSNINGEN:

—388;256-felet, och även >Screen-Felet eliminerat som det ser ut|20Okt2014:}

Procedure TmMoveSkipWHITE;

var

  A,B:           TBitMap;

  Pa,Pb,aP,bP:   PChar;

  TMa,TMb:       TMemoryStream;

  W,H,x:         Integer;

  R:             TRect;

  aR,aG,aB:      Byte;

begin with Form1 do begin

          TMa:= TMemoryStream.Create;

          TMb:= TMemoryStream.Create;

          A:= TBitMap.Create;

          B:= TBitMap.Create;

          W:= Image2.Width;

          H:= Image2.Height;

          R:= Rect(0,0,W,H);

{Tar ut bakgrunden för jämförelse:}

          aR:= GetRValue(bgCol);

          aG:= GetGValue(bgCol);

          aB:= GetBValue(bgCol);

       try

{SpecialRutin, etablerar bitmap:ens dimensioner:}

          BitmapRect(R,A);

          BitmapRect(R,B);

          A.Canvas.Brush.Color:= clWhite;

          A.Canvas.FillRect(R);

{Preparera|BehandlingsBitMaps:}

          A.PixelFormat:= pf32bit;

          B.PixelFormat:= pf32bit;

{FLYTBILD=Källbild|Im2¦Copy|Im1¦MarkRect|TillA|}

          A.Canvas.CopyRect(R,Image2.Canvas,R);

{PREPARERA MÅLBILDENS BITMAP FÖR BAKGRUNDSELIMINERING:}

          B.Canvas.Brush.Color:= bgCol;

          B.Canvas.FillRect(R);

          B.TransparentColor:= bgCol;

          B.Transparent:= True;

{SPARA TILL MemoryStreamen så att vi kan göra ändringar därifrån:}

          A.SaveToStream(TMa); //Källbilden|FlytBildeIm2.

          B.SaveToStream(TMb); //Målbilden|SkipBackGroundColorResult.

{TILLDELA MemoryStreamen en AdressPekare - vårt HuvudTransportMedel i ändringarna:}

          Pa:= TMa.Memory;

          Pb:= TMb.Memory;

{Samma som hela BMP-strukturen|Med BitMapInfoHeader: börjar från pos54:}

{En enda lång sammanhängande DataMinnesAdressRad för hela bilden:}

             for x:= 0 to H*W - 1 do begin

                bP:= Pb + 54 + 4*x;

                aP:= Pa + 54 + 4*x;

{SkipBgCOL:}

                if (aR=Byte((aP +2)^))

                and(aG=Byte((aP +1)^))

                and(aB=Byte((aP +0)^))

                then Continue;

{BG-ytan kvarlämnas orörd på B för slutlig transparens via Im1Draw:}

                Byte((bP +2)^):= Byte((aP +2)^);

                Byte((bP +1)^):= Byte((aP +1)^);

                Byte((bP +0)^):= Byte((aP +0)^);

 

             end;{endForX}

{FINALIZING:}

{UPPDATERA ÄNDRINGARNA TILL den ursprungliga bitmap:en|B:}

{Förbered MemoryTillBitMap:}

{Börjar från MinnesPekarensPosition - funkar inte om annat än från 0:}

          TMb.Position:= 0;

{UPPDATERAR ÄNDRINGARNA:}

          B.LoadFromStream(TMb);

{FlytbildsPreparering|OK|Bilden ritas ut|X0|Y0 flytbildsparametrar:}

          Image1.Canvas.Draw(X0,Y0,B);

       finally

         A.Free;

         B.Free;

         TMa.Free;

         TMb.Free;

       end;

end; {endWithForm1}

end; {endTmMoveSkipWHITE}

{Överflyttad kopia från T2014|20Okt2014.}

 

 

Med denna justering har ytterligare fel ännu inte observerats

— För vidare observation (20Okt2014).

   Kommentar allmänt DELPHI4 ScanLines:

— Något är alldeles tydligt uppenbart fel — antingen i DELPHI4 eller på annat sätt i förening med SAcanLine-funktionen (subrutin) som under speciellt taskiga lägen åstadkommer försmädliga AccessViolationAvbrott — i sig inte farliga, men tillräckliga för att den egentliga funktionen hoppas över. Vi har nu SÄKERT studerat TRE ScanLineFELområden: 16;64-felet, 388;256-felet och StörreÄnScreen-felet.

— Samtliga dessa ScanLineFEL försvinner (tydligren) om TMemoryStream-metoden används istället, vilket garanterar fullständigt ScanLineOberoende (med exakt 32-bitars minneshantering).

 

Invertera:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Invertera flytbilden — tangent I

Vi lägger till ytterligare en sådan enkel, elementär, flytbildsinstans: FormKeyDown CaseBlocket utan Shift, alla variabler finns redan:

 

 {INVERTERA FLYTBILDEN|11Sep2014:}

      Ord('I'): begin

                  with Image2.Canvas do begin

                   R:= Rect(0,0,Image2.Width,Image2.Height);

                   B:= TBitMap.Create;

                   try

                    BitMapRect(R,B);

                    B.Assign(Image2.Picture);

                    CopyMode:= cmDstInvert;

                    CopyRect(R,B.Canvas,R);

                   finally

                    B.Free;

                   end;

                   CopyMode:= cmSrcCopy;

                  end;{endWithIm2Canvas}

                  UpdateMergeFlags;

                end;

 

Checking: Vänster: Efter Ctrl+V och F5. Höger: Efter I:

 

 

Utomordentligt. Att Inverteringsfunktionen fungerar perfekt kontrollerar vi genom att lägga den inverterade kopian över originalet och köra MoveAND (Tangent C). Korrekt funktion (ickeINV + INV = helsvart) betyder att bilderna tar ut varandra med resultat i helsvart:

 

Vänster: Efter Ctrl+V No2 och Tangent C; helsvart. Höger: Ctrl+Numpad1(Step1p), Ctrl+ PilHö:

 

 

Vi ser att KONTURER BÖRJAR FRAMTRÄDA med minsta förskjutning på en pixel RightDown i leden XY mellan två varandra inverterade original.

— Det är också programgrunden till det mycket eftertraktade — och mycket effektiva fotoredigeringsverktyget/bildbehandlingsverktyget — SHARPENING generellt.

 

 

RGBpalett:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Enkel RGB-palett F4 med FärgUnderPeken — Numpad|0

 

Ofta, ibland, behöver vi få i varje fall ungefärliga färger som motsvarar NATRLIGA SPEKTRUMets färger:

 

 

Färgkartan ovan (från FinishingTouch Windows 95-eran) — orienterad vertikalt — finns inlagd som Palett3.bmp i projektkatalogen för P2.

 

Tangent F4 tar fram den på Image1 och lägger ut den som flytbild enligt DELPHI4-koden på FormKeyDown F4, CaseKeyBlocket:

 

 {PALETT RGBflytbild:}

      VK_F4:    begin

                  S:= 'Palett3.bmp';

                  if not FileExists(S) then exit;

                  Image2.Picture.LoadFromFile(S);

                  Image3.Picture.LoadFromFile(S);

                  {Anger bildmåttet, PanelLabeln överst i mitten:}

                  Panel4.Caption:=IntToStr(Image2.Width)

                  +';'+IntToStr(Image2.Height);

                  X0:=0; Y0:=0;

                  CRs:=Image2.ClientRect;

                  CRd:=CRs;

                  OffsetRect(CRd,X0,Y0);

                  Image3.BoundsRect:= Image2.BoundsRect;

                  Image3.Canvas.CopyRect(CRs,Image1.Canvas,CRd);

                  Image1.Canvas.CopyMode:= cmSrcCopy;

                  Image1.Canvas.CopyRect(CRd,Image2.Canvas,CRs);

                  SetCursorPos(Left+Panel1.Left+32, Top+Panel1.Top+Y0+26+4);

                  ClipBoardImported:= True;

                end;{endF4}

 

ClipBoardImported här inlagt om man vill optimera fönsterstorleken (F5) för detta speciella fall.

— Med F5 (P2-fönstret här roterat +90°) ger F4-kommandot Palett3 intagen med Peken förinställd på svart:

 

 

  

 

 

Fortsättningen i FÄRGANALYSATOR RGB|HSB OCH FÄRGREDIGERARE med DELPHI4.

 

VisuellaSpektrumPalettRitningen i DELPHI4 — Pixels-metoden

A,N,Z: Integer; Blocket nedan ger endast EN horisontell rad, denna har sedan dubblerats i höjd:

 

A:= 255;

Z:= 0;

with Image1.Canvas do

begin

for N:= 0 to 127 do Pixels[N+0*127,50]:= RGB(A,2*N,Z);

for N:= 0 to 127 do Pixels[N+1*127,50]:= RGB(A-2*N,A,Z);

for N:= 0 to 127 do Pixels[N+2*127,50]:= RGB(Z,A,2*N);

for N:= 0 to 127 do Pixels[N+3*127,50]:= RGB(Z,A-2*N,A);

for N:= 0 to 127 do Pixels[N+4*127,50]:= RGB(2*N,Z,A);

end;{endWithIm1}

Normal — också maximal färgmättnad

 

TONSEKTORERNA avgränsas och bestäms av ELEMENTARSPEKTRALFÄRGERNA resp

0|RÖD(255,0,0) 1|GUL(255,255,0) 2|GRÖN(0,255,0) 3|INDIGO(0,255,255) 4|BLÅ(0,0,255) 5|VIOLETT(255,0,255), sedan

åter mot 6|0|RÖD(255,0,0) — inte medtagen ovan: ingår inte i det visuella spektrumet:

 

 

För att få med även sista intervallet 5 som sluter spektrumcirkeln mot rött igen, ska det skrivas med B från 255 mot 0:  5: C:= RGB(A,Z,A-K*N);.

— Vi måste ha med detta sista intervall för att fullt kunna omvandla mellan RGB och HSB: H [eng. Hue, FärgTON eller FärgNYANS] i HSB är just ovanstående visuella spektrum.

 

VisuellaSpektrumPalettRitningen i DELPHI4 — SetPixel-metoden (10ggr snabbare)

H: THandle; A,N,Z,K,I: Integer; C: TColor; — K-faktorn bestämer TonSektorIntervallet, här förkortat till 51pixels:

— Programblocket nedan ger endast en horisontell pixelrad, denna sedan dubblerad i höjd:

 

H:= Image1.Canvas.Handle;

A:= 255;

Z:= 0;

C:= 0;

K:= 5;

I:= 255 div K;

with Image1.Canvas do

begin

 for TonSektor:= 0 to 4 do

  for N:= 0 to I do begin

   case TonSektor of

    0: C:= RGB(A,K*N,Z);

    1: C:= RGB(A-K*N,A,Z);

    2: C:= RGB(Z,A,K*N);

    3: C:= RGB(Z,A-K*N,A);

    4: C:= RGB(K*N,Z,A);

   end;

   SetPixel(H, N + TonSektor*I, 50, C);

  end;

end;{endWithIm1}

 

VisuellaSpektrumPalettRitningen i DELPHI4 — ScanLine-metoden (1000ggr snabbare)

B: TBitMap;  R: TRect;  Pb: PByteArray;  bR,bG,bB: Byte; K,A,Z,N,TonSektor,I,J: Integer; :

— Programblocket nedan ger hela bilden som ovan:

 

R:= Rect(0,0,300,10);

B:= TBitMap.Create;

try

 BitMapRect(R,B);

 B.PixelFormat:= pf32Bit;

 B.Canvas.Brush.Color:= clWhite;

 B.Canvas.FillRect(R);

       A:= 255;

       Z:= 0;

       K:= 5;

       I:= 255 div K;

       bR:=0; bG:=0; bB:=0;

 Pb:= B.ScanLine[0];

       for TonSektor:= 0 to 4 do

        for N:= 0 to I do begin

         J:= 4*(N + TonSektor*I);

         case TonSektor of

          0: begin bR:= A;     bG:= K*N;   bB:= Z;   end;

          1: begin bR:= A-K*N; bG:= A;     bB:= Z;   end;

          2: begin bR:= Z;     bG:= A;     bB:= K*N; end;

          3: begin bR:= Z;     bG:= A-K*N; bB:= A;   end;

          4: begin bR:= K*N;   bG:= Z;     bB:= A;   end;

         end;

         Pb[J + 2]:= bR;

         Pb[J + 1]:= bG;

         Pb[J + 0]:= bB;

        end;

 for I:= 0 to 20 do

 Image1.Canvas.Draw(0,I,B);

finally

 B.Free;

end;

 

Fortsätt i EditColors — färgredigering och analys RGB|HSB.

 

 

 

Fel1664

Elementär flytbildsteknik — grundexempel i DELPHI4

 

112;320-felet

16;64-felet — ScanLineFEL i DELPHI4

ScanLines

 

   B:= TBitMap.Create;

   try

     for W:= 16 to 200 do

     for H:= 16 to 200 do

     begin

       R:= Rect(0,0,W,H);

       BitMapRect(R,B);

       ClipBoard.Assign(B);

       D:= Ord('V');

       FormKeyDown(Self,D,[ssCtrl]);

       ClipBoard.AsText:= IntToStr(W)+';'+IntToStr(H);

       D:= Ord('R');

       FormKeyDown(Self,D,[]);

     end;

   finally

     B.Free;

   end;

 

AVBROTT sker för alla n heltalskombinationer n16;n64 eller n64;n16 som pixelmått på den källbild som ska vändas horisontellt eller roteras +90° OM ScanLine-funktionen i DELPHI4 används: första stannar på 16;64 med ett programexekveringsdavbrott av typen AccessViolation: funktionen har havererat.

— Används istället en TMemoryStream — direkt ByteÅtkomst i BitMapens Dataminne — ger ovanstående testgenomgång (33.856=184² stycken rektanglar testade) perfekt avbrottsfri körning. Tangenterna Ctrl+V tar in Urklippskopian, och R leder till rotationsblocket. Det faktum att TMemoryStream klarar biffen utan protester styrker misstanken (starkt) att:

32bitars rektangelvändning på ByteBaserad Horisontell — men inte vertikal — dataomkastning och överföring av bilddata från Längd till Bredd och vice versa INTE är korrekt utformat i DELPHI4. Det blir svårt att se saken på annat sätt med TMemoryStreamBeviset. Procedurerna följer nedan.

 

I samband med test av ovanstående (15Sep2014) — P2-fönstrets mått efter anpassning (F5) till den importerade Palett3-bilden — med import av det fönstret till T2014 och sedan FlipHorizontal och Rotate90, visas ACCESS VIOLATION:

— Har aldrig tidigare inträffat under de snart 15 år som programformen använts.

— Inspektion visade att det är bildmåtten som är kritiska:

— Skiljer det på bara en pixel, visas inte felet.

 

TBitMapInfoHeader:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Efter Inside WINDOWS FILE FORMATS s57, Tom Swan, Sams Publishing 1993

 

15-18 4             1.DWORD        biSize: ......................   Longint;

19-22 4             2.LONG            biWidth: ...................    Longint;

23-26 4             3.LONG            biHeight: ..................    Longint;

27-28 2             4.WORD           biPlanes: .................    Word;

29-30 2             5.WORD           biBitCount: ..............    Word;

31-34 4             6.DWORD        biCompression: ......     Longint;

35-38 4             7.DWORD        biSizeImage: ...........    Longint;

39-42 4             8.LONG            biXPelsPerMeter: ....   Longint;

43-46 4             9.LONG            biYPelsPerMeter: ....    Longint;

47-50 4             10.DWORD      biClrUsed: ................   Longint;

51-54 4             11.DWORD      biClrImportant: ........   Longint;

 

RGB-formatets datalängd — formen oven samma som via DELPHI4:s TMemoryStream.

Pixeldatats offsetadress är 54. DataPekaren ska ställas på P + 54 för att få bildens första pixel (B-värdet i första färggruppen BGR#).

 

FlipHorizontal:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Proceduren nedan testad OK 15|16Sep2014 — FlipHorizontal:

 

Procedure TMFlipHorizontal(SourceIm: TImage);

var

  A,B:           TBitMap;

  Pa,Pb,iP,P:    PChar;

  TMa,TMb:       TMemoryStream;

  W,H,x,y,Sx,Ex: Integer;

begin with Form1 do begin

  TMa:= TMemoryStream.Create;

  TMb:= TMemoryStream.Create;

  A:= TBitMap.Create;

  B:= TBitMap.Create;

  try

   {SpecialRutin, etablerar bitmap:ens dimensioner:}

    BitmapRect(CRs,A);

    BitmapRect(CRs,B);

{Källbilden|Im2¦Copy|Im1¦MarkRect|TillA|CRd|s|Avgörande:}

    A.Canvas.CopyRect(CRd,SourceIm.Canvas,CRs);

{Preparera|BehandlingsBitMaps:}

    H:= A.Height;

    W:= A.Width;

    A.PixelFormat:= pf32bit;

    B.PixelFormat:= pf32bit;

    B.Canvas.Brush.Color:= clWhite;

    B.Canvas.FillRect(CRs);

{SPARA TILL MemoryStreamen så att vi kan göra ändringar därifrån:}

    A.SaveToStream(TMa);

    B.SaveToStream(TMb);

{TILLDELA MemoryStreamen en AdressPekare - vårt HuvudTransportMedel i ändringarna:}

    Pa:= TMa.Memory;

    Pb:= TMb.Memory;

{Samma som hela BMP-strukturen|Med BitMapInfoHeader: börjar från pos54:}

    for y:= 0 to H-1 do begin

     Sx:= (H-1-y)*W;

     Ex:= (H - y)*W - 1;

     for x:= Sx to Ex do begin

      iP:= Pb + 54 + 4*(Ex + Sx - x);

       P:= Pa + 54 + 4*x;

      Byte((iP +2)^):= Byte((P +2)^);

      Byte((iP +1)^):= Byte((P +1)^);

      Byte((iP +0)^):= Byte((P +0)^);

     end;{endForX}

    end;{endForY}

{FINALIZING:}

   {UPPDATERA ÄNDRINGARNA TILL den ursprungliga bitmap:en|B:}

   {Börjar från MinnesPekarensPosition - funkar inte om annat än från 0:}

    //TMa.Position:= 0|Behövs inte|InteÄndrad.

    TMb.Position:= 0;

{UPPDATERAR ÄNDRINGARNA:}

    B.LoadFromStream(TMb);

{FlytbildsPreparering|OK|Bilden ritas ut:}

    Image1.Canvas.CopyMode:= cmSrcCopy;

    BitmapToIm(B);

    //Image1.Canvas.Draw(X0,Y0,B);

  finally

    A.Free;

    B.Free;

    TMa.Free;

    TMb.Free;

  end;

end;{endWithForm1}

end;{endTMFlipHorizontal}

 

 

Proceduren nedan testad OK 15|16Sep2014 — RgB till BgR:

 

ExchangeRgB:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

RGB till BGR:

 

{Färgvändningen|BitMap A|RGB till BGR:}

{Samma som ovan men enbart med BitMap A och ByteKoden:}

R:= Byte((aP +2)^);

Byte((aP +2)^):= Byte((aP +0)^);

Byte((aP +0)^):= R;

 

TestatOK 16Sep2014. Bägge procedurerna fungerar ännu så länge utan observerade fel.

 

NEJ. NYLIGEN ÄNNU ETT AVBROTT. FlipHorizontal.

— Nej. Berodde på att jag hade satt

 

    H:= SourceIm.Height;

    W:= SourceIm.Width;

 

istället för korrekta

 

    H:= A.Height;

    W:= A.Width;

 

EFTERSOM SourceIm i fallet MarkRect är Image1 (kopiering sker från större till mindre):

— Avbrott med MarkRect och sedan direkt H, medan en kopia som går till Image2 inte gav avbrott.

— Ordnat, således. DELPHI4-dramatik.

 

— Märkvärdigt är det. Testade nyligen återigen Rot90° på »gamla ScanLineBlocket» Unit1 T2014:

— Nu uppför sig den delen ytterligare rebelliskt: för varje rotation minskas originalbildens bild, inte bildytan, med 1 pixelrad, så att det som blir kvar minskar med antalet rotationer.

— Det var något nytt. Har aldrig tidigare visat sig.

Mitt fel: R:= Bounds(0,0,CRd.Bottom+1,CRd.Right+1); REST efter nyligen genomförda TEST.

— Tas tillägget bort fungerar det som tidigare, frånsett 16;64-felen.

— Rättat med hjälp av gamla Project2002.

   DELRESULTAT AV FELSÖKNINGEN 16;64-felet:

   EFTERSOM TMemoryStream INTE visar något fel på 112;320-Fönstret från P2, men att ScanLineBlocket gör det, UPPSTÅR MISSTANKEN att 16;64-felet ligger hos DELPHI4 i ScanLine-algoritmen.

— Nästa steg blir att försöka utforma en TMemoryStreamProcedur för Rot90°-fallet också. Det kommer att avgöra.

 

TMrot90:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Proceduren nedan testad OK 15|16Sep2014 — Rotate90°:

 

Procedure TMScanRot(SourceIm: TImage);

var

  A,B:           TBitMap;

  R:             TRect;

  P,iP,Pa,Pb,

  TMa,TMb:       TMemoryStream;

  W,H,x,y,Sx,Ex,

  iX:            Integer;

begin with Form1 do begin

  TMa:= TMemoryStream.Create;

  TMb:= TMemoryStream.Create;

  A:= TBitMap.Create;

  B:= TBitMap.Create;

  try

{Målbildens roterade källrektangel|Width¦Height¦omvända|Rect eller Bounds här egalt:}

    R:= Bounds(0,0,CRd.Bottom,CRd.Right);

   {SpecialRutin, etablerar bitmap:ens dimensioner:}

    BitmapRect(CRs,A);

    BitmapRect(R,  B);

{Källbilden|Im2¦Copy|Im1¦MarkRect|TillA|CRd|s|Avgörande:}

    A.Canvas.CopyRect(CRd,SourceIm.Canvas,CRs);

{Preparera|BehandlingsBitMaps:}

    H:= A.Height;

    W:= A.Width;

    A.PixelFormat:= pf32bit;

    B.PixelFormat:= pf32bit;

    B.Canvas.Brush.Color:= clWhite;

    B.Canvas.FillRect(CRs);

{SPARA TILL MemoryStreamen så att vi kan göra ändringar därifrån:}

    A.SaveToStream(TMa);

    B.SaveToStream(TMb);

{TILLDELA MemoryStreamen en AdressPekare - vårt HuvudTransportMedel i ändringarna:}

    Pa:= TMa.Memory;

    Pb:= TMb.Memory;

{Samma som hela BMP-strukturen|Med BitMapInfoHeader: börjar från pos54:}

{Överföringen från A till B|Sx = WH - W - Wy| Ex = WH - 1 - Wy|iX=x-Sx= 0 1 2 3 .. :}

    for y:= 0 to H-1 do begin

      Sx:=(H-1-y)*W;

      Ex:= Sx + W-1;

      for x:= Sx to Ex do begin

       iP:= Pb+54 + 4*(y + H*(x-Sx));

        P:= Pa+54 + 4*x;

       Byte((iP +2)^):= Byte((P +2)^);

       Byte((iP +1)^):= Byte((P +1)^);

       Byte((iP +0)^):= Byte((P +0)^);

     end;{endForX}

    end;{endForY}

{FINALIZING:}

   {UPPDATERA ÄNDRINGARNA TILL den ursprungliga bitmap:en|B:}

   {Börjar från MinnesPekarensPosition - funkar inte om annat än från 0:}

    TMb.Position:= 0;

{UPPDATERAR ÄNDRINGARNA:}

    B.LoadFromStream(TMb);

{FlytbildsPreparering|OK|Bilden ritas ut|S|GlobalSträng|SpeciellSluthantering:}

    S:='ScanRotCompleted';

    BitmapToIm(B);

    //Image1.Canvas.Draw(X0,Y0,B);

  finally

    A.Free;

    B.Free;

    TMa.Free;

    TMb.Free;

  end;

end;{endWithForm1}

end;{endTMScanRot|16Sep2014.}

 

 

Ex = Sx + W–1 = (H-1-y)*W + W–1 = WH-W-Wy  + W–1 = WH-Wy –1 = W(H-y) –1

 

Enda som skiljer FlipHorizontal från Rotate90° i DataLoopen är

FH    iP:= Pb + 54 + 4*(Ex + Sx - x);

R90   iP:= Pb + 54 + 4*(y + H*(x-Sx));

 

Råformen i 90°-rotationerna är

 

          Byte((Pb+54+(4*y)+ 4*(iX)*H +2)^):= Byte((Pa+54+ 4*x+2)^);

          Byte((Pb+54+(4*y)+ 4*(iX)*H +1)^):= Byte((Pa+54+ 4*x+1)^);

          Byte((Pb+54+(4*y)+ 4*(iX)*H +0)^):= Byte((Pa+54+ 4*x+0)^);

med

iX = x – Sx = x – W(H–1–y);

Pb+54+(4*y)+ 4*(iX)*H

kan då förenklas via

Pb+54+ 4*(y+ H*iX) = Pb+54+ 4*(y+H*(x–Sx)) = iP;

 

— Hur gick det, då, med 112;320-fönstret?

 

 

Perfekt.

— En säker TESTFORM som bevisar att 90°-rotationerna fungerar perfekt är att ta ut en himmelsdel på ett foto och rotera 4 ggr:

 

 

Efter fjärde 90°-rotationen ska uttaget smälta in tillbaka på exakt plats det togs ut ifrån — och »utan förluster», dvs., inga borttagna kantlinjer (eller annat) får förekomma. Bilden ovan efter test av föregående TMScanRot.

 

— Det skulle, då, betyda: ScanLine-funktionen i DELPHI4 är inte korrekt utformad.

— Det är heller ingen avundsvärd programuppgift att försöka göra en KOMMERSIELL programprodukt som ska täcka samtliga möjliga PixelFormat-fall (man behöver nog avancera till närmast GUD för den uppgiften);

— En öppen svårighet — och som visat sig så svår att ens få visuell koll på (nu först efter 15 år[!]) — KAN just vara BILDREKTANGELOMVÄNDNINGAR kontra den YTTERST känsliga minneshanteringsalgoritmen: missas en enda BIT eller Byte är det KÖRT: Access Violation @Address ... . Säkert som amen i kyrkan.

— ScanLine i DELPHI4 FUNGERAR FÖR ÖVRIGT PERFEKT

— om inga andra Det-Tar-Runt-15år-Att-Upptäcka-Felen-Övningar är att vänta.

   Så länge man inte laborerar med VÄNDNING av bildrektanglarna — FlipHorizontal Rotera90°: FlipVertical lider inte av 16;64-felet — verkar allt fungera utan några som helst problem: inga observerade avbrott (än).

 

 

 

Separat test med rektanglar från 16×16p upp till 200×200p gjordes nyligen (Speciell TestLoop 16Sep2014|T2014 33856st Rect) på TMScanRot: helt galant felfritt. Det styrker bevisningen.

 

   B:= TBitMap.Create;

   try

     for W:= 16 to 200 do

     for H:= 16 to 200 do

     begin

       R:= Rect(0,0,W,H);

       BitMapRect(R,B);

       ClipBoard.Assign(B);

       D:= Ord('V');

       FormKeyDown(Self,D,[ssCtrl]);

       ClipBoard.AsText:= IntToStr(W)+';'+IntToStr(H);

       D:= Ord('R');

       FormKeyDown(Self,D,[]);

     end;

   finally

     B.Free;

   end;

ClipBoardKopian för W;H ifall avbrott skulle ske (vetskap om var). Här: NoProblemo.

 

 

 

 

EditColors

Senast vald Bakgrund(B)|Förgrund(N) redigeras — sliderna 1. dras med musen, 2. flyttas med PilVäHö Ctrl+ NumPad|123 sätter 1 5 20 steg, 3. numeriskt med Alt+nnn; PilUppNer Radval; CapsLock Kolumnval RGB|HSB, markering visar parameter; Enter Verkställer, Esc ångrar&avslutar, R åter utgångsläget.

 

 

 

EditColors:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

FÄRGANALYSATOR RGB|HSB

med FÄRGREDIGERARE i DELPHI4

Panel12|Image5

 

 

 

 

·          MusVäNER på aktuell knapp|slid ger skjutreglageeffekt

·          Alt+ nnn med siffror nnn på sifferbordet  NumPad|0-9 skriver in värdena direkt på vald post (markerat som ovan, himmelsblå bakgrund)

·          Piltangenter VäHö steppar (flyttar alla RGB-slider om HSB är valt) i steg om 1 5 eller 20 pixels med inställning Ctrl+ NumPad|1 2 3

·          Piltangenter UppNer växlar rad R(H)G(S)B(B), CapsLock växlar kolumn RGB eller HSB

·          Enter sätter senast vald färg fgCol FÖRGUND eller bgCol BAKGRUND; Esc avslutar utan ändring (Space+Enter öppnar)

·          Tanget O = Enter = SättNy; Tangent C = Escape = Exit = AvslutaUtanÅtgärd; Tangent R = TaOmRedigeringenFrånInslagspunkten.

·          EditColorsPanelen visas längst ner till höger i P2:

·          RedigeraFärgen är den som SIST valdes av fgCol=Förgrundsfärgen=LillaRutanNedreVä Tangent N eller bgCol=Bakgrundsfärgen=StoraRutanUnder Tangent B (närmsta tangenten Bakom N)

 

 

 

Ett mer eller mindre OUNDGÄNGLIGT bildhanteringsverktyg är tillgång till FÄRG + detaljerad färgförklaring:

— Det innebär att man alltid i alla lägen har tillgång till

 

·         en FÄRGANALYSATOR — enkelt elementär inblick i spektralmatematiken (HSB) tillsammans med RGB-värden

·         en FÄRGREDIGERARE integrerad för att PÅ ENKLASTE SÄTTET få fram exakt önskad färg (bakgrund eller förgrund) med numeriska värden, om så önskas

 

DELPHI4kod:

1. Unit1C, ny — EditColorsUnit

2. uses Unit1C, tillägg till usesBlocket Unit1

3. Panel12 och Image5|alClient|Visible|AutoSize=False: Unit1; ecTimer: deklareras på Unit1C efter usesBlocket enligt

 

type

  TecTimer = class(TTimer)

  Procedure OnEditColors(Sender: TObject);

  end;

 

och som skrivs (vidare nedan) i ImplementationBlocket Unit1C på det (notera) speciella sättet

 

Procedure TecTimer.OnEditColors(Sender: TObject);

...

(och Vilket PunktPåt Garanterar att nybörjaren har det JÄTTESVÅRT med Delphi[4] de första 50 åren ...: i princip OMÖJLIGT att »räkna ut logiskt» på förhand de olika sätten: man måste ha detaljerad inblick i Delphis/Pascals type-class-system).

 

och som deklareras GlobalVariabel i Unit1C i varIABELblocket efter usesBlocket

 

       ecTimer:     TecTimer;

 

ecTimer (EditColorsTimer) skapas separat Unit1 på FormCreate (sist) enligt

 

       ecTimer:= TecTimer.Create(nil);

       with ecTimer do begin

       Enabled:= False;

       OnTimer:= OnEditColors;

       end;

(nil [”ingenting”] anger att ecTimer saknar Parent, annars ska ParentEgenskapen stå i parentesen, typ ”Form1”; nil används generellt för att säkra att en variabel INTE upptar någon direkt plats i datorminnet, typ [innan ..Create] BitMap1:= nil;.

— Delphihjälpen [Overview of pointers] skriver på nil:

”The reserved word nil is a special constant that can be assigned to any pointer. When nil is assigned to a pointer, the pointer doesn’t reference anything.”)

 

och entledigas från DatorMinnet på Unit1 FormClose enligt

 

           ecTimer.Free;

 

Allt det övriga finns i Unit1C med procedurerna i typeBlocket

 

  Procedure vRGBtoHSB(var C1,C2,C3: Integer);

  Procedure vHSBtoRGB(var C1,C2,C3: Integer);

  Procedure EditColors(var Key: Word; Shift: TShiftState);

 

och sedan deras ordinarie kodblock i Implementationsdelen, tillsammans med den nu särskilt originellt, till skillnad från de andra, angivna TecTimer.OnEditColors (först i kön):

— Proceduren nedan  TecTimer.OnEditColors är INTE deklarerad globalt i Unit1C:

— Vi gör så ibland — skriver LOKALA procedurer närmast ÖVER en undre MasteProcedur (alternativt, sätter in den som en INRE NÄSTAD procedur i mastern direkt efter varBlocket): Mastern kan använda en icke globalDeklarerad procedure OM, och endast då, denna står FÖRE i aktuell Unit. Står den efter, känns den inte igen.

 

{EditColorscTimer|Används för att simulera MouseMove|RGB|HSB-sliderna på Image5:}

Procedure TecTimer.OnEditColors(Sender: TObject);

var

  W: Word;

begin with Form1 do begin

  if not Panel12.Visible then exit;

  if GetKeyState(VK_LBUTTON)<=-127 then

  begin

    ecTimer.Interval:= 10;

    W:= Ord(#2);

    EditColors(W,[]);

    exit;

  end

  else

  begin

{STANDby:}

    ecTimerON:=False;

    ecTimer.Interval:= 100;

{RENSA Alt+NumPad0..9 Input om Ångra före 3 inslag:}

    if (GetKeyState(VK_MENU)>-127)

    and(Length(S)<3)

    and(KeyNP09)

    then

    begin

      S:= '';

      KeyNP09:=False;

      Label1.Caption:= 'Clear';

    end;{endIfÅngraInslagFöre3}

  end;{endEcTimerStandBy}

end;{endWithForm1}

end;{endTForm1ecTimerTimer|14Jul2014 Uni5 T2014|17Sep2014|Uni1C|P2}

{ANVÄNDS till RGB|HSB|ScrollRows istf.MouseMove.}

 

RENSA-blocket: Inmatning av RGB eller HSB-värden kan ske manuellt med Alt+NumPad 0..9 i TRE siffror (0 skrivs då 000):

— ÅNGRAR man sig (efter typ 53 ... jä..) rensas inslaget med visningen ”Clear” på Statusradens Label1 om man släpper upp Alt-tangenten. Sedan är det bara att slå om.

— Typen Alt+”5378” resulterar bara i ”255”, övriga sätt ignoreras.

 

 

Se DELPHI4-koden för de tre övriga procedurerna ovan särskilt i

EditColorsUnit1C.

 

 

 

 

 

Ytterligare OUNDGÄNGLIGA FLYTBILDStillståndsSÄTT med enkla tangenttryckningar [Se HIMRV]:

 

H                     vändHorisontellt

V                     vänd Vertikalt

R                      Rotera i steg om +90°

M                     Tona (motVitt)[Vid programstart, börja från 50%]

NumPad+           ÖkaToning (max100)

NumPad–           MinskaToning (min0)

 

Space+H           färgvänd RGB till BGR (MånskensFärger blir SolUppgångsDito och v.v.)

NumPad0           visa färgenUnderPeken i RGB(0-255) och HSB(0-255)

Space+NumPad0 Som ovan men kopieras i text till Urklipp

 

Sedan vi infört STECKREKTANGEL med uttagsfunktioner:

 

Ctrl+D               Duplicera

Ctrl+Shift+D       FlyttaUt (samma som ovan men originalet under raderas)

Ctrl+C               Kopiera till Urklipp

Ctrl+X               Klipp ut till Urklipp

Ctrl+Delete        Radera (alt.ev. Ctrl+E[rase])

 

Ctrl+ T              Text (läggs ut som flytbild | Fet, kursiv, AllaSTORAellersmå m.m.)

T                     Som ovan efter första (senast finns kvar)

 

Unit2=Unit1A

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Med ovanstående, vidare utvidgningar i DEN ELEMENTÄRA (DELPHI4) programkoden för ett ELEMENÄRT flytbildsverktyg (P2), introduceras vi (allt mer) för SYSTEMATISKA STRUKTURER:

— kodblock med inre SPECIELLA sammansättningar och inbördes beroenden som INTE DIREKT hör till formytans komponenter.

— För att (något) skilja dessa kodDepartement åt (Programfönstrets KodBasic, Bildhanteringens olika KodBasic) väljer jag här att införa en ny komplementär Unit (’Unit2’) till Unit1, och som här kommer att namndöpas om till Unit1A (med ev. vidare Unit1B, Unit1C, osv.) — för att reservera ev. kommande ENKLA grepp åt DELPHI4 själv om man vill införa ytterligare FORMytor i projektet: DELPHI4 tillordnar då automatiskt Form2 med Unit2, Form3 med Unit3, osv. Det finns ingen anledning att bryta tillfället att utnyttjade den enkla självserveringen.

 

Alt+F, New ..., Unit, Enter resulterar i att DELPHI4 lägger ut ett nytt kodfönster som ser ut så:

 

 

Vad DELPHI4 lägger till automatiskt vid införd ny enhet.

 

Inget mer. Inget mindre.

— Kompilera direkt med Ctrl+F9, och kör sedan med F9:

DELPHI4 lägger nu DIREKT som första händelse ut en dialogbox:

 


Notera att DELPHI4 ovanstående dialog INTE kommer upp MED KATALOGEN MARKERAD SAMMA SOM DET SENAST ÖPPNADE PROJEKTET utan det senaste sparade projektet.

   Är man inte uppmärksam på det — man tror (utan att direkt läsa dialogens detaljer) att »sparkatalogen är samma som projektkatalogen» — kan ett ENTER resultera i att den nya enheten sparas på helt fel plats. I så fall: Bläddra fram till aktuell projektkatalog och spara om, och gå sedan till felkatalogen och radera felsparfilen därifrån manuellt.

 

— Ändra namnet ”Unit2” till ”Unit1A”, sedan Enter.

— Avsluta sedan P2 (Alt+F4).

 

Vi ska nu PREPARERA den nya Enheten Unit1A och göra den direkt KODKOMPATIBEL med Unit1 (samt sedan, vidare ev. övriga Units).

— För att göra det behöver vi göra olika tillägg (Här markerade särskilt med ljusgult och orange text). Dessa finns sammanställda nedan med inlagda förklaringsblock som rubricerar de olika partierna.

   För särskild förklaring, se sammanställning i UnitRubrikerna.

   Hela blocket med enheter i uses nedan är samtliga de (främsta) som behövs (min erfarenhet) för att kunna koda DELPHI4 optimalt. Testa att ta bort de som inte behövs: Om någon enhet tas bort som behövs, meddelar DELPHI det (går inte att kompilera, då).

Procedure ExecuteHRV har här lagts in som inledande exempel på hur procedurer ska deklareras i en separat Unit i DELPHI4.

 

Unit1A, UnitRUBRIKERNA

Elementär flytbildsteknik — grundexempel i DELPHI4

 

 

Den nydöpta enheten Unit1A..

 

unit Unit1A;

{Unit1A|12Sep2014|BildHanteringsRutiner|TillUnit1.}

 

interface

uses

Windows, Messages, SysUtils, WinTypes, WinProcs,

Classes, Graphics, Forms, Dialogs, StdCtrls,

FileCtrl,ExtCtrls, Controls, ShellAPI,

Math, Clipbrd, ComCtrls;

{GlobalVARIABLES|Constants .....................................:}

 

{GlobalVARIABLES|Constants ......................................}

{GlobalPROCEDURES & Functions — GlobalDeclarations .............:}

Procedure ExecuteHRV(SourceIm: TImage; Key: Word);

{GlobalPROCEDURES & Functions — GlobalDeclarations ..............}

implementation

uses

Unit1;

{MOTSVARANDE »uses Unit1A» måste tillfogas på Unit1 antingen

1. i |uses| direkt efter |interface| LÄNGST UPP — om detaljer som står

explicit under Unit1:s Interface används, eller

2. som närmast ovan i Unit1-makens Implementation|Uses.

— Se dessa detaljer utförligt i DELPHI4help,

Circular unit references: regeln är att Unit:s inte får referera

varandra i samma uses-block|Delphi vägrar kompilera då.}

{LocalVARIABLES|Constants ......................................:}

 

{LocalVARIABLES|Constants .......................................}

{ActualPROCEDURES & Functions - - - - - - - - - - - - - - - - - :}

Procedure ExecuteHRV(SourceIm: TImage; Key: Word);

begin

exit;

end;

{ActualPROCEDURES & Functions - - - - - - - - - - - - - - - - - .}

end.

 

Motsvarande uses Unit1A på Unit1 införs ENKLAST OCH KONSEKVENT ALLTID (alternativet i 1 ovan) längst upp enligt

 

 

— Importera ovanstående tillägg, och VERIFIERA med kompilering Ctrl+F9 och körning F9:

— Projektet P2 är nu färdigt att använda den nya enheten med full pedal.

 

Lägg märke till följande:

— olikheten i Unit1A mot Unit1 i GlobalaLokalaProcedurDeklarationer:

 

OM vi —som i Unit1 som HAR en egen Form1 där alla Globalt deklarerade ImplementationProcedurNamn MÅSTE börja med ”TForm1.” — testar att deklarera ImplementationProceduren ovan på formen

 

Procedure TForm1.ExecuteHRV(SourceIm: TImage; Key: Word);

 

vägrar DELPHI4 att kompilera med upplysningen

 

[Error] Unit1A.pas(30): ';' expected but '.' found

[Error] Unit1A.pas(13): Unsatisfied forward or external declaration: 'ExecuteHRV'

[Fatal Error] P2.dpr(6): Could not compile used unit 'Unit1A.pas'

— Tryck TAB (eller klicka på kodfönstret) för att komma tillbaka till kodfönstret sedan Meddelandefönstret klickats av,

 

Alltså:

— I den separata Unit-enheten (här Unit1A som ovan):

— Utelämna den inledande typen ”TForm1.” från alla procedurNamn;

— För att kunna ANVÄNDA Form1-komponenterna SOM OM UNIT1A NU VORE EN INTEGRERAD DEL AV UNIT1, börja ALLTID varje procedur i begin med exemplifierat

 

Procedure ExecuteHRV(SourceIm: TImage; Key: Word);

begin with Form1 do begin

exit;

end;{endWithForm1}

end;{endExecuteHRV}

 

Då fungerar det utmärkt.

— Skriv sedan alla variabler i Unit1A — SOM SKA KUNNA LÄSAS AV OCKSÅ Unit1 — längst upp i det anvisade blocket ovan [GlobalVariables].

— Alternativt, för enstaka förekomster, om inte ”with Form1 do begin” används, måste varje Form1-komponents användning i Unit1A inledas med ett ”Form1.”,

t.ex. Form1.Label1.Caption:= .. .

 

FÖRUTSATT att nu en viss procedur är deklarerad Globalt i Unit1A, kan den nås OMEDELBART från Unit1 med direktkommandot, här exemplifierat (t.ex. från FormKeyDown),

 

ExecuteHRV(Image2,Key);

 

Det är allt.

— På samma sätt kan Unit1A använda och utnyttja alla globalt deklarerade procedurer i Unit1 med samma typ av enkla anrop, t.ex. BitMapRect(R,B); förutsatt R och B känns igen internt i Unit1A, eller kopplar globalt för alla.

 

Alla föregående ev. deklarerade lokala globaler i Unit 1 (under Implementation) och som nu kommer att användas av Unit1A måste vi flytta upp i GlobalSektionen (efter Uses under Interface) Unit1, annars syns de inte för Unit1A. Vi förutsätter här den operationen bekant och anger den inte vidare i framställningen: DELPHI4 säger i vilket fall till om vi glömt något.

 

   OK. Då kan vi börja resan in till det verkliga äventyret.

 

StreckRektangeln, markeringar för uttag

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Streckrektangeln

 

Första åtgärden, vidareutvecklingen av Projekt P2.

— Vi fortsätter att hålla oss ett tag till i Unit1.

 

Utan ett verktyg — streckrektangel — som kan markera eller ta ut önskade partier för olika ändamål — kopiera, duplicera, radera, vända, rotera, invertera — är det ELEMENTÄRA flytbildsprogrammet fortfarande bara ett halvdant dito.

 

Hur DELPHI4-kodar man en streckrektangel — vars bildyta sedan kan användas för olika bildhanteringsändamål?

 

Image1MouseDown (Image1 Object Inspector F11, OnMouseDown, Ctrl+Enter) skriver vi in koden (globala variabler Xm0,Ym0,Xm1,Ym1: Integer;)

 

  if (Button=mbLeft)then

    begin

      Image1.Cursor:= crArrow;

      Image1.Canvas.Pen.Width:= 1;

 

      SCRIm1.TopLeft:=Image1.ClientOrigin;

      SCRIm1.BottomRight:= Point(

      Image1.ClientOrigin.X +

      Image1.ClientWidth+1,

      Image1.ClientOrigin.Y +

      Image1.ClientHeight+1 );

 

{INITIERAR STRECKREKTANGELN:}

      Image1.Canvas.Rectangle(Xm0,Ym0,Xm1,Ym1);

{Raderar föregående, XOR-styrd|Här markeras startpunkten:}

      Xm0:=X; Ym0:=Y;

      Xm1:=X; Ym1:=Y;

    end;{endIfButtonLeft}

    {Om man sätter koordinatblocket på denna plats,}

    {Xm0:=X; Ym0:=Y;

     Xm1:=X; Ym1:=Y;}

    {konserveras streckrektangeln med högerklick,

    den kan sedan inte klickas bort.}

 

Image1MouseMove verkställs motsvarande

 

  //Screen.Cursor:= MC1

  {STRECKREKTANGELN:}

  with Image1.Canvas do begin

    Brush.Color:=clWhite;

    with Pen do begin

     Color:= 0;

     Style:= psDot;

     Mode:= pmNotXor;

    end;

    if(Shift=[ssLeft])and(PtInRect(SCRIm1,CursP)=True)then

    begin

{RADERAR GAMLA:}

      Rectangle(Xm0,Ym0,Xm1,Ym1);

{RitarNya:}

      Rectangle(Xm0,Ym0,X,Y);

      Xm1:=X; Ym1:=Y;

    end;

  end;{endWithIm1Canvas}

 

Variablerna X|Y|m0|1 och CursP är speciella och kan deklareras i Unit1 som lokala (i ImplementationBlocket)

 

var

  CursP:             TPoint;

  Xm0,Xm1,Ym0,Ym1:   Integer; {MarkRectMouseMove}

 

Vi kontrollerar att streckrektangeln fungerar (Peken nedan förminskad kopia av originalet för att inte distrahera|sammanblanda med normalpeken):

 

 

Perfekt funktion.

— Men Peken kan flyttas utanför fönsterkanterna, medan streckrektangeln begränsas av bildytan (Image1).

— Hur löser vi det?

 

EMELLERTID:

— Streckrektangeln, men inte Peken, håller sig inom Image1-ytan med MusVä nedtryckt: Peken kan obehindrat flyttas ut utanför det aktuella avgränsade MÖJLIGA streckrektangelområdet.

   Vi skulle vilja att Peken stannar inom Image1-området, oberoende av vidare musrörelser.

ClipCursor:

Elementär flytbildsteknik — grundexempel i DELPHI4

 

ClipCursor

Peken stannar inom Image1-området så länge MusVänster hålls nedtryckt,

oberoende av vidare musrörelser

 

Funktionen för det finns särskilt i Windows API och heter ClipCursor.

— VARNING:

— Följande DELPHI4-kod är utformad, testat, prövad och fungerar oklanderligt:

Vill du försöka själv att testa med ClipCursorFunktionen — den är WindowsSystemBaserad, vilket innebär att den är en DeladResurs för alla WindowsProgram:

— missar man något i kod här, kommer datorns muspekarfunktion garanterat att låsa sig, garanterat.

Enda sättet i så fall: dra ur pluggen: starta om datorn.

— OM du vill testa DELPHI4 på det sättet, FÖRBERED DIG:

— se först till att ha typ AKTIVITETSHANTERAREN öppen, för säkerhets skull, eller annat möjligt TANGENTTILLGÄNGLIGT hjälpfönster som kan nås och på den vägen ev. starta om datorn.

— Är du det minsta tveksam till den typen av Test: avstå.

 

Här är DELPHI4-koden som får ClipCursor att fungera perfekt:

 

1.

En global Pointer-rektangel deklareras på Unit1: PR: PRect;

En global Sim1-rektangel deklareras på Unit1: Sim1: TRect;

En global SCRIm1-rektangel finns här redan insatt från tidigare;

 

Försöker man deklarera alla de 3 ClipCursor-variabler som ingår — SCRIm1, Sim1, PR — i skilda sektioner lokalt och globalt ger DELPHI4 meddelandet vid försök att kompilera Ctrl+F9 (här Sim1 separat lokalt),

[Hint] Unit1.pas(98): Variable 'Sim1' is declared but never used in 'Unit1'”.

   Det är den visst det. Men ...

— »Never used» — Trots att Koden (3 nedan) på MouseUp använder just Sim1.

   Flyttas alla tre ingående variablerna till samma variabelsektion upphör mysterierna.

— Vi väljer här att sätta dem globalt, vilket ger snabbast möjliga åtkomst i alla lägen.

2.

På Image1MouseDown lägger vi till koden

 

        GetMem(PR, SizeOf(Sim1));

        PR^:= SCRIm1;

        Screen.Cursor:= crDefault;

        ClipCursor(PR);

3.

Image1MouseUp (Image1 Object Inspector F11, OnMouseUp, Ctrl+Enter) verkställs den slutliga operationen i kod som avslutar ClipCursorSessionen:

 

 if Button=mbLeft then begin

   FreeMem(PR,SizeOf(Sim1));

   PR:= nil;

   ClipCursor(PR);

   CurFlag:=0;

   UpdateInsertFlag;

 end;

 

Ovanstående insatt ska fungera oklanderligt, från första stund.

— Vi återkommer senare till CurFlag (deklarerad Curflag: Integer;) och UpdateInsertFlag.

 

 

Nu stannar Peken kvar inom Image1-ytan så länge MusVä hålls nedtryckt.

— Klick på bildytan tar bort rektangeln

 

Streckrektangeln, StädKod

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Städkod för streckrektangel

 

Vi behöver säkra ev. rester om vi ångrar initieringen eller bara vill ta bort streckrektangeln utan att göra något, utom att klicka av den med MusVäKlick förstås.

— Koden nedan införd på FormKeyDown med DELS Escape-tangenten och DELS på F2 (Rensa hela bildytan):

 

      VK_ESCAPE:begin

                  if Xm0<>Xm1 then

                  {StreckRektangel finns:}

                  begin

                   Image1.Canvas.Rectangle(Xm0,Ym0,Xm1,Ym1);

                   Xm0:=Xm1;

                   Ym0:=Ym1;

                   CurFlag:= 0;

                   UpdateInsertFlag;

                  end;

                end;{endEscape}

 

— Vi återkommer senare till CurFlag och delproceduren UpdateInsertFlag.

   Streckrektangeln dras med XNor-funktion (påverkar inte övrigt bildunderlag). Vilket betyder att om en XNor-rit finns kvar vi bara behöver upprepa den en gång till (på samma koordinater som sist) för att få bort den.

   Tillägget på VK_F2 för att säkra att streckrektangeln inte kommer tillbaka vid nästa MusVäKlick:

 

  {NOLLSTÄLL INSÄTTNINGSPUNKTER FÖLR FLYTBILD:}

                  X0:=0;

                  Y0:=0;

  {NOLLSTÄLL STRECKREKTANGEL:}

                  Xm0:=Xm1;

                  Ym0:=Ym1;

 

Streckrektangeln, Synkronisering

Elementär flytbildsteknik — grundexempel i DELPHI4

 

Streckrektangelns SYNKRONISERING

 

 

Ytterligare en detalj krävs för att streckrektangeln ska fungera — utan att DELPHI4 pajar ihop — MED SÄKRADE MUSDRAGNINGSMÅTT:

 

Delphi pajar ihop helt och hållet om Left/Top blir mindre än

Right/Bottom — på CopyRect-funktionen. Med

Rectangle-funktionen har det aldrig funnits

några sådana problem.

 

   Med alla möjliga SÄTT att dra streckrektangeln uppkommer, strax, möjligheten att få NEGATIVA KOORDINATVÄRDEN i bestämningen av början och slutpunkter: vi vill definitivt inte ha sådana. Delphiprogrammet kraschar då.

— Streckrektangeln måste under alla omständigheter kunna redovisa EN ENTYDIGT DEFINIERAD LeftTopRightBottom-rektangel i slutänden som anvisar arbetsytan för samtliga möjliga efterföljande operationer.

 

Så här ser den väl beprövade säkringen för synkronisering av streckrektangelns fyra koordinater ut:

 

{Allmänna villkor · Endast om streckrektangel finns:}

 X:=0; Y:=0; X1:=0; Y1:=0;

 if (Xm0<>Xm1)and(Ym0<>Ym1) then

 begin

  if