un TBitmap ha un TCanvas nel quale si può disegnare
se le immagini sono 32x32 basta disegnarle l'una accanto all'altra calcolando l'offset
qualcosa tipo:
var xb: TBitmap;
begin
xb := TBitmap.Create;
while not dataset.eof do begin
src_bitmap := dataset.FieldByName...
xb.Canvas.Draw((dataset.recno-1)*32, 0, src_bitmap);
dataset.next;
end;
end;
Allora, due prove le ho fatte:
Ho creato 2 funzioni: una con imagemagick ed una con il Canvas: ricevono una lista di Stream e mandano in uscita una bitmap.
La prima funziona e la seconda no.
Posto il codice della seconda
function Streams2Bitmap2(Lista: TList):TBitmap;
var
i:integer;
bitmap:TBitmap;
p:TPicture;
begin
bitmap:=TBitmap.Create;
for i :=0 to Lista.Count - 1 do
begin
p:=TPicture.Create;
p.PNG.LoadFromStream(TStream( Lista.Items[i]));
bitmap.Canvas.Draw(i*32, 0, p.Bitmap);
p.Clear;
end;
Result:=bitmap;
end;
il Bitmap lo visualizzo sul campo di una griglia ed uso:
Grid.Canvas.StretchDraw(fixRect,TBitmap(bitmap));
all' interno di una funzione:
OnDrawColumnCell
di una TrxDbGrid.
Visto che con imagemagik funziona (passo e ricevo gli stessi valori per entrambe le funzioni), probabilmente sto sbagliando qualcosa qui......
Paolo
ciao
questi giorni sono incasinato, non riesco a provare, ma intanto ti volevo dire di provare il codice sotto
non dovrebbe cambiare nulla nel risultato finale purtroppo
però nel codice che hai postato l'istruzione p:=TPicture.Create, crea un oggetto ad ogni iterazione ma non viene mai liberato
function Streams2Bitmap2(Lista: TList):TBitmap;
var
i:integer;
bitmap:TBitmap;
p:TPicture;
begin
bitmap:=TBitmap.Create;
p:=TPicture.Create;
try
for i :=0 to Lista.Count - 1 do
begin
p.PNG.LoadFromStream(TStream( Lista.Items[i]));
bitmap.Canvas.Draw(i*32, 0, p.Bitmap);
p.Clear;
end;
finally
p.Free;
Result:=bitmap;
end;
end;
Edit:
posta anche il codice con cui popoli Lista che passi come parametro, può essere utile a capire
riguardando il codice credo manchi SetSize
prova in questo modo:
function Streams2Bitmap2(Lista: TList):TBitmap;
var
i:integer;
bitmap:TBitmap;
p:TPicture;
begin
bitmap:=TBitmap.Create;
bitmap.SetSize(Lista.Count * 32, 32);
p:=TPicture.Create;
try
for i :=0 to Lista.Count - 1 do
begin
p.PNG.LoadFromStream(TStream( Lista.Items[i]));
bitmap.Canvas.Draw(i*32, 0, p.Bitmap);
p.Clear;
end;
finally
p.Free;
Result:=bitmap;
end;
end;
Ok, ora funziona. Mancava:
bitmap.SetSize(Lista.Count * 32, 32);
Posto il codice che uso per popolare la lista:
// funzione richiamata dall' evento OnDrawColumnCell della colonna interessata in RxDbGrid
procedure LoadSelectedBlob2SelectedField2(Sender: TObject;const Rect: TRect; Column: TColumn;ZROQCustom:TZReadOnlyQuery);
var
fixRect : TRect;
tmpLeft : integer;
bmpWidth : integer;
wi:Double;
proporzione:Double ;
lstBLOB : TList;
Grid: TDBGrid;
i:Integer;
corpo:TCorpo;
bitmap:TBitmap;
maestrie:TList;
begin
fixRect := Rect;
bitmap:=TBitmap.Create;
lstBLOB:=TList.Create;
corpo:=TCorpo.Create;
Grid := TDBGrid (Sender);
//corpo e maestrie sono oggetti che contengono i dati da trasformare nella lista sequenziale.
//I dati sono ricavati dall' elaborazione di alcune query all' interno di questi oggetti
// Ricava tuttle maestrie che hanno lo stesso valore di IdCorpo
corpo.SetIdCorpo(Grid.DataSource.DataSet.FieldByName('IDCorpo').AsInteger);
maestrie:=corpo.GetMaestrie;
//aggiunge gli stream alla lista
for i :=0 to maestrie.Count - 1 do
begin
lstBLOB.Add(TStream(TMaestria(maestrie.Items[i]).GetSimbolo));
end;
//funzione che trasforma gli stream in un unico bitmap
bitmap:=Streams2Bitmap2(lstBLOB);
//area in cui viene ssovrascritto il bitmap
proporzione:= (rect.Bottom-rect.Top)/32;
fixRect.Top:=Rect.Top+1;
fixRect.Bottom:=fixRect.Bottom-2;
fixRect.Left:=Rect.Left;
wi:=bitmap.Width;
fixRect.Right := fixRect.Left + trunc(wi*proporzione);
//cella dove viene scritto il bitmap
Grid.Canvas.StretchDraw(fixRect,TBitmap(bitmap));
end;
Grazie a tutti.
Paolo
Devo farti notare che anche in questo caso manca la liberazione delle risorse.
In lazarus, come in delphi, le istanze degli oggetti in memoria vengono gestiti in modo diverso a seconda che si tratti di oggetti posizionati con il drag/drop su un form/datamodule o che si tratti di oggetti creati a runtime.
In particolare:
- oggetti posizionati con drag/drop: la gestione della memoria (allocazione e deallocazione) è automatica e non ti devi preoccupare di nulla
- oggetti creati a runtime: devi preoccupari di liberare la memoria una volta che non sono più necesari
Infatti Delphi non ha un garbage collector (e secondo me è una fortuna ;)) e quindi, ogni volta che scriviamo del codice, bisogna sempre domandarsi se abbiamo liberato tutte le risorse occupate.
Sembra chissacchè ma la soluzione è elementare e semplicissima: adottare in modo maniacale una try ... finally ogni volta che si crea un'istanza.
Nel tuo codice, nel momento in cui scrivi:
begin
bitmap:=TBitmap.Create;
lstBLOB:=TList.Create;
corpo:=TCorpo.Create;
[... resto delle istruzioni...]
end;
devi immediatamente far seguire all'allocazione delle risorse un blocco try/finally dove, dopo la keyword finally, liberi il tutto.
Esempio:
begin
itmap:=TBitmap.Create;
lstBLOB:=TList.Create;
corpo:=TCorpo.Create;
try
[... resto delle istruzioni...]
finally
itmap.Free;
lstBLOB.Free;
corpo.Free;
end;
end;
In questo modo sei sicuro di liberare sempre la memoria, anche in caso di eccezione e di crash della tua applicazione!