Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: TWICImage pictures get scrambled when rotating


This question is not answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 10 - Last Post: Sep 22, 2016 6:45 AM Last Post By: Lajos Juhasz
Mikael Lenfors

Posts: 99
Registered: 3/6/01
TWICImage pictures get scrambled when rotating  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 13, 2016 8:00 AM
Hello!

I'm using the function below to load images from disk and write them to database. The problem is this works 100% ok on my developement PC (Windows 7 pro 64) but on several other computers with same operating system and memory, the Pictures get scrambled. Looks like colored lines across the Picture.

What can be the reason, and how to solve it?

Best regards, Mikael

Procedure TPicturesMaintenanceForm.B_AddClick(Sender: TObject);
Begin
If OpenPictureDialog.Execute Then
Begin
Try
FDQueryDoc.Close;
FDQueryDoc.SQL.Clear;
FDQueryDoc.SQL.Add('Select *');
FDQueryDoc.SQL.Add('From Pictures (NoLock)');
FDQueryDoc.SQL.Add('Where Id = - 1');
FDQueryDoc.Open;
FDQueryDoc.Append;
FDQueryDoc.FieldByName('FileName').AsString := ExtractFileName(OpenPictureDialog.FileName);
TBlobField(FDQueryDoc.FieldByName('Image')).LoadFromFile(OpenPictureDialog.FileName);
FDQueryDoc.Post;
FDQueryDoc.Close;
Except
On E: Exception Do
Begin
ShowMessage('Error Reading image! (' + E.Message + ')');
End;
End;
End;
End;

Mikael Lenfors

Posts: 99
Registered: 3/6/01
Re: TWICImage pictures get scrambled when rotating  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 13, 2016 8:03 AM   in response to: Mikael Lenfors in response to: Mikael Lenfors
Sorry, the subject was wrong, but i get the same problem when loading Pictures from database, rotating them and then writing them back to the database.
I figured it had nothing to do with the rotating but with the Writing to database?!

Mikael
Mikael Lenfors

Posts: 99
Registered: 3/6/01
Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 15, 2016 5:24 AM   in response to: Mikael Lenfors in response to: Mikael Lenfors
Ok, I continued testing and narrowed it down to the following. Below is a simplified procedure where I get the error.

First I load a Image from file (1),
Then I save the TImage to a stream (2).
Then I use my standard function StreamToImage witch imports the stream back into another TImage. This woks just fine! (3)

Then I send the stream into my database record (4)
Then I load the Stream back from Database (5)
Then I send the stream into a new TImage (6). This image looks scrambled with coloured lines straight over the Picture.

This problem only appeares on some computers! My developement computers newer has an error!

Please, any advise?

Best regards, Mikael

procedure TPicturesMaintenanceForm.B_ReplaceClick(Sender: TObject);
Var MemoryStream: TMemoryStream;
Begin
MemoryStream := TMemoryStream.Create;

Image1.Picture := Nil;
Image2.Picture := Nil;

Image.Picture.LoadFromFile(FileName); // (1)

Image.Picture.Graphic.SaveToStream(MemoryStream); // (2)
StreamToImage(MemoryStream, Image1); // (3) This Image1 looks just fine
MemoryStream.Clear;

// Updating database (4)
FDQueryTemp.Close;
FDQueryTemp.SQL.Clear;
FDQueryTemp.SQL.Add('Select *');
FDQueryTemp.SQL.Add('From Pictures (NoLock)');
FDQueryTemp.SQL.Add('Where Id = 1');
FDQueryTemp.Open;
FDQueryTemp.Edit;
Image.Picture.Graphic.SaveToStream(MemoryStream);
MemoryStream.Position := 0;
TBlobField(FDQueryTemp.FieldByName('Image')).LoadFromStream(MemoryStream);
FDQueryTemp.Post;

FDQueryTemp.Refresh;
MemoryStream.Clear;
TBlobField(FDQueryTemp.FieldByName('Image')).SaveToStream(MemoryStream); // (5)
StreamToImage(MemoryStream, Image2); // (6) This Image2 gets scrambled after a roundtrip to the database

FDQueryTemp.Close;
MemoryStream.Free;
End;

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 15, 2016 9:36 AM   in response to: Mikael Lenfors in response to: Mikael Lenfors
Mikael wrote:

Then I send the stream into my database record (4)

Did you verify that the bytes you put into the database are the exact same
bytes you get back out of it?

Image.Picture.Graphic.SaveToStream(MemoryStream);
StreamToImage(MemoryStream, Image1);

You did not show the code for StreamToImage(). I assume that it automatically
seeks the stream back to position 0 before reading from it? And that it
handles multiple image types? What kind of images are you loading to begin
with?

TBlobField(FDQueryTemp.FieldByName('Image')).LoadFromStream(MemoryStream);

You should be using the CreateBlobStream() method instead of accessing TBlobField
directly:

var
  Stream: TStream;
 
...
 
FDQueryTemp.Edit;
try
  Stream := FDQueryTemp.CreateBlobStream(FDQueryTemp.FieldByName('Image'), 
bmWrite);
  try
    Image.Picture.Graphic.SaveToStream(Stream);
  finally
    Stream.Free;
  end;
  FDQueryTemp.Post;
except
  FDQueryTemp.Cancel;
  raise;
end;
 
...
 
FDQueryTemp.Refresh;
Stream := FDQueryTemp.CreateBlobStream(FDQueryTemp.FieldByName('Image'), 
bmRead);
try
  StreamToImage(Stream, Image2);
finally
  Stream.Free;
end;


--
Remy Lebeau (TeamB)
Mikael Lenfors

Posts: 99
Registered: 3/6/01
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 15, 2016 11:36 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Ok, thanks.

I rewritten it using CreateBlobStream instead. I'll try it tomorrow when I can access the computers having the problem again.
Works finns on my developement computer :-)

If ít doesn't work I will log the streams written and read so that I can compare them.

My function for loading Images from stream is as below. It seems to work nicely :-)

Best regards, Mikael

Function StreamToimage(Const Stream: TStream; Const Image: TImage): Boolean;

Var JPEG: TJPEGImage;
PNGImage: TPNGImage;
GIFImage: TGIFImage;
WICImage: TWicImage;

Begin

Result := False;

Image.Picture := Nil;

Try

// Test WIC format

Try

WICImage := TWICImage.Create;

Stream.Position := 0;
WICImage.LoadFromStream(Stream);
Image.Picture.Assign(WICImage);

Result := True;

Finally

WICImage.Free;

End;

Except

Try

// Test PNG format

Try

PNGImage := TPNGImage.Create;

Stream.Position := 0;
PNGImage.LoadFromStream(Stream);
Image.Picture.Assign(PNGImage);

Result := True;

Finally

PNGImage.Free;

End;

Except

Try

// Test BMP format

Stream.Position := 0;
Image.Picture.Bitmap.LoadFromStream(Stream);

Result := True;

Except

Try

// Test GIF format

Try

GIFImage := TGIFImage.Create;

Stream.Position := 0;
GIFImage.LoadFromStream(Stream);
Image.Picture.Assign(GIFImage);

Result := True;

Finally

GIFImage.Free;

End;

Except

Try

// Test JPEG format

Try

JPEG := TJPEGImage.Create;

Stream.Position := 0;
JPEG.LoadFromStream(Stream);
Image.Picture.Assign(JPEG);
Image.Visible := True;

Result := True;

Finally

JPEG.Free;

End;

Except

// Unknown file format, hide picture
Image.Visible := False;

End; // Failed reading JPEG

End; // Failed reading GIF

End; // Failed reading bmp

End; // Failed reading PNG

End; // Failed reading WIC

End;

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 15, 2016 12:31 PM   in response to: Mikael Lenfors in response to: Mikael Lenfors
Mikael wrote:

My function for loading Images from stream is as below. It
seems to work nicely :-)

Wow, that is a horrible implementation. I would use something more like
this instead:

function StreamToImage(const Stream: TStream; const Image: TImage): Boolean;
const
  PngMagic: array[0..7] of Byte = ($89, $50, $4E, $47, $0D, $0A, $1A, $0A);
  BmpMagic: array[0..1] of Byte = ($42, $4D);
  Gif87Magic: array[0..5] of Byte = ($47, $49, $46, $38, $37, $61);
  Gif89Magic: array[0..5] of Byte = ($47, $49, $46, $38, $39, $61);
  JpgMagic: array[0..1] of Byte = ($FF, $D8);
var
  Magic: array[0..7] of Byte;
  NumBytes: Integer;
  StartPos: Int64;
 
  procedure TryLoadImage(GraphicClass: TGraphicClass);
  var
    Graphic: TGraphic;
  begin
    Graphic := GraphicClass.Create;
    try
      Stream.Position := StartPos;
      Graphic.LoadFromStream(Stream);
      Image.Picture.Assign(Graphic);
    finally
      Graphic.Free;
    end;
  end;
 
begin
  Result := False;
  Image.Picture := nil;
 
  NumBytes := Stream.Read(Magic, SizeOf(Magic));
  if NumBytes > 0 then
    Stream.Seek(-NumBytes, soCurrent);
 
  StartPos := Stream.Position;
 
  try
    if (NumBytes = 8) and CompareMem(Magic, PngMagic, 8) then
    begin
      // Load PNG format
      TryLoadImage(TPNGImage);
    end
    else if (NumBytes >= 6) and (CompareMem(Magic, Gif87Magic, 6) or CompareMem(Magic, 
Gif89Magic, 6)) then
    begin
      // Load GIF format
      TryLoadImage(TGIFImage);
    end
    else if (NumBytes >= 2) and CompareMem(Magic, JpgMagic, 2) then
    begin
      // Load JPEG format
      TryLoadImage(TJPEGImage);
    end
    else if (NumBytes >= 2) and CompareMem(Magic, BmpMagic, 2) then
    begin
      // Load BMP format
      TryLoadImage(TBitmap);
    end
    else
    begin
      // Load WIC format
      TryLoadImage(TWICImage);
    end;
    Result := True;
  except
    Result := False;
  end;
 
  Image.Visible := Result;
end;


Or, even better, store the actual TGraphic class name in the DB with the
image data, and then simply read the name back out so you know which class
to use instead of trying to guess:

procedure TPicturesMaintenanceForm.B_ReplaceClick(Sender: TObject);
var
  MemoryStream: TMemoryStream;
  BlobStream: TStream;
  GraphicClassName: ShortString;
begin
  Image1.Picture := nil;
  Image2.Picture := nil;
 
  Image.Picture.LoadFromFile(FileName);
 
  GraphicClassName := Image.Picture.Graphic.ClassName;
 
  MemoryStream := TMemoryStream.Create;
  try
    MemoryStream.WriteBuffer(GraphicClassName, 1+Length(GraphicClassName));
    Image.Picture.Graphic.SaveToStream(MemoryStream);
    StreamToImage(MemoryStream, Image1);
  finally
    MemoryStream.Free;
  end;
 
  FDQueryTemp.Close;
  FDQueryTemp.SQL.Clear;
  FDQueryTemp.SQL.Add('Select *');
  FDQueryTemp.SQL.Add('From Pictures (NoLock)');
  FDQueryTemp.SQL.Add('Where Id = 1');
  FDQueryTemp.Open;
  FDQueryTemp.Edit;
 
  try
    BlobStream := FDQueryTemp.CreateBlobStream(FDQueryTemp.FieldByName('Image'), 
bmWrite);
    try
      BlobStream.WriteBuffer(GraphicClassName, 1+Length(GraphicClassName));
      Image.Picture.Graphic.SaveToStream(BlobStream);
    finally
      BlobStream.Free;
    end;
    FDQueryTemp.Post;
  except
    FDQueryTemp.Cancel;
    raise;
  end;
 
  FDQueryTemp.Refresh;
 
  BlobStream := FDQueryTemp.CreateBlobStream(FDQueryTemp.FieldByName('Image'), 
bmRead);
  try
    StreamToImage(BlobStream, Image2);
  finally
    BlobStream.Free;
  end;
 
  FDQueryTemp.Close;
end;
 
function StreamToImage(const Stream: TStream; const Image: TImage): Boolean;
var
  Len: Byte;
  GraphicClassName: ShortString;
  Graphic: TGraphic;
begin
  Result := False;
  Image.Picture := nil;
 
  Stream.ReadBuffer(Len, 1);
  SetLength(GraphicClassName, Len);
  Stream.ReadBuffer(GraphicClassName[1], Len);
 
  try
    Graphic := TGraphicClass(FindClass(GraphicClassName)).Create;
    try
      Graphic.LoadFromStream(Stream);
      Image.Picture.Assign(Graphic);
    finally
      Graphic.Free;
    end;
    Result := True;
  except
    Result := False;
  end;
 
  Image.Visible := Result;
end;
 
...
 
initialization
  RegisterClass(TJPEGImage);
  RegisterClass(TPNGImage);
  RegisterClass(TGIFImage);
  RegisterClass(TWicImage);


--
Remy Lebeau (TeamB)
Lajos Juhasz

Posts: 801
Registered: 3/14/14
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 15, 2016 11:03 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

...
<div class="jive-quote">   if (NumBytes = 8) and CompareMem(Magic, PngMagic, 8) then
    begin</div>
...

I bet this is a bug and should be:

     if (NumBytes >= 8) and CompareMem(Magic, PngMagic, 8) then
     begin
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 16, 2016 11:15 AM   in response to: Lajos Juhasz in response to: Lajos Juhasz
Lajos wrote:

...
if (NumBytes = 8) and CompareMem(Magic, PngMagic, 8) then begin
 
...

I bet this is a bug and should be:

if (NumBytes >= 8) and CompareMem(Magic, PngMagic, 8) then
begin

No, it is not. The Magic[] array is 8 bytes in size to begin with, and Read()
is being asked to read no more than 8 bytes, so NumBytes can never be greater
than 8. The use of '=' instead of '>=' was intentional. Now, if Mikael
decided in the future to support a new image format that uses a signature
greater than 8 bytes in size, then switching to '>=' would be needed.

--
Remy Lebeau (TeamB)
Mikael Lenfors

Posts: 99
Registered: 3/6/01
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 22, 2016 2:36 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Ok, now I tried to narrow it down. I tried rewriting the code with CreateBlobStream as you suggested but I still get the same problem.

Se the code below. code here simplified as much as possible.
I have a perfectly fine Picture in a TImage.
(1) I create a write stream to my BlobField and save the image there. record is posted.
(2) Then I take the same stream and save it to a disk file. This file looks fine when viewed with Windows preview, file size 180 936 byte.
(3) Then I read the Image back from database (first a new select statement i performed),
(4). Then I take the stream and save it to a disk file. This file is corrupted, from about half way down from the top and all the way down to the bottom when viewed with Windows preview, file size 180 930 byte (6 bytes smaller).

The database is a SQL server 10.0.5538.0 and a 12.0.4100.1 (same problem on both).

The problem only appeares on some computers, my developement machine newer has a problem. All clients Running Windows 7 Pro 64.

Please advise! really got stuck here.

Best regards, Mikael

// (1) Here the picture is fine and located in "Image" and saved to database
Stream := FDQueryTemp.CreateBlobStream(FDQueryTemp.FieldByName('Image'), bmWrite);
Image.Picture.Graphic.SaveToStream(Stream);

// (2) For debugging I flush the stream content to disk, this "SaveImage.jpg" file is just fine!
Stream.Position := 0;
FileStream := TFileStream.Create(GetWorkingFolder + 'SaveImage.jpg', fmCreate);
FileStream.CopyFrom(Stream, Stream.Size);

----

// (3) Here I read my image back from database
Stream := FDQueryDoc.CreateBlobStream(FDQueryDoc.FieldByName('Image'), bmRead);

// (4) For debugging I flush the stream content to disk, this "LoadImage.jpg", file is corrupted!
Stream.Position := 0;
FS := TFileStream.Create(GetWorkingFolder + 'LoadImage.jpg', fmCreate);
FS.CopyFrom(Stream, Stream.Size);

Mikael Lenfors

Posts: 99
Registered: 3/6/01
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 22, 2016 3:51 AM   in response to: Mikael Lenfors in response to: Mikael Lenfors
I actually think I solved it!

I downloaded lates versions of SQL Server Native Client 11 and SQL Server Native Client 13 and installed them on the machines having problem. Problem solved!
Is this a known problem with some older Native Client version?

Is it enough to install Native Client 13? Are they used for different things? Different database versions?

Best regards, Mikael
Lajos Juhasz

Posts: 801
Registered: 3/14/14
Re: Pictures get scrambled when saved to database  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Sep 22, 2016 6:45 AM   in response to: Mikael Lenfors in response to: Mikael Lenfors
Mikael Lenfors wrote:

em with some older Native Client version?

Is it enough to install Native Client 13? Are they used for different
things? Different database versions?

For this question you should start a new thread in
embarcadero.public.delphi.database.firedac. As you've wrote it's about
the SQL Server Native client and nothing to do with the graphichs.
Asking question in correct place gives you better chance to get an
answer.

Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02