TBitmap->LoadFromStream failed with Win XP - c++builder

I'm using C++ Builder XE3 to develop a graph editor. All of the editing and drawing capabilities are made in a DLL that is loaded by the end user applications.
To store information about the available graph objects I use a SQLite database. That database contains BMP icons that are loaded into a TImageList at run-time.
Everything works fine with Win-7, Win-8 and Win-vista but with Win-XP a "Floating point division by 0" occurs when loading the bitmap. I use a temporary memory stream to load the blob from the database and then load it into a temporary TBitmap which is used to add the new icon into the final TImageList.
Here is the function used to do so...
void TIcons::AddMaskedBitmap( TImageList *ptImgList, unsigned char *pucIcon, unsigned int uiSize )
{
TMemoryStream *ptMemStream;
// Use a memory stream
ptMemStream = new TMemoryStream();
ptMemStream->Write( pucIcon, uiSize );
ptMemStream->Position = 0;//Seek( ( int )0, ( unsigned short )soBeginning );
// Load using the cached bmp object
m_ptBmp->Transparent = true;
#warning "floatting point division by 0 error with WinXP"
m_ptBmp->LoadFromStream( ptMemStream ); // floatting point division by 0 error with WinXP
// m_ptBmp->LoadFromFile( ".\\d.bmp" ); // works
// Create a mask
m_ptBmpMask->Assign( m_ptBmp );
m_ptBmpMask->Canvas->Brush->Color = m_ptBmp->TransparentColor;
m_ptBmpMask->Monochrome = true;
// Add it to the list
ptImgList->Add( m_ptBmp, m_ptBmpMask );
// Free mem
m_ptBmp->FreeImage();
m_ptBmpMask->FreeImage();
delete ptMemStream;
}
I've traced the TBitmap::LoadFromStream function and the exception occurs in the CreateDIBSection function.
To make sure the loaded bitmap files are saved using the right encoding I've tried to load them using the TBitmap::LoadFromFile function and it works fine, so I think there's something wrong with the TBitmap::LoadFromStream function but I can't figure out what !
If anyone has an idea...
Thanks.

LoadFromFile is implemented by creating a file stream, and passing that to LoadFromStream. This, if the contents of your memory stream are the same as the contents of the file, then the call to LoadFromStream will succeed.
Thus the only sane conclusion is that the contents of the memory stream are invalid in some way.

The bitmap stored into the database is encoded using the BITMAPV4HEADER structure which is supposed to be supported since Win95/NT4 but there's something wrong.
It works fine if I encode the bitmap using the BITMAPINFOHEADER structure which is an older version of bitmap encoding which does not contain color space information.

Just found out a solution that works for me.
My problem was that software that was developed on Win7, when running on XP was throwing the division by 0 error when loading one of my BMPs.
It turns out that the problematic BMP was saved using Win7 Paint (other BMPs that were ok were saved from Gimp).
All I needed to do to fix it was to open this BMP on XP Paint and save it from there.

Related

Resize / Convert an image from a stream with ImageResizer

I'm trying to figure out how to convert an image from a stream with ImageResizer (http://imageresizing.net/).
I have tried something like this.
Stream s = WebRequest.Create("http://example.com/resources/gfx/unnamed.webp").GetResponse().GetResponseStream();
ImageBuilder.Current.Build(s, "~/resources/gfx/photo3.png", new ResizeSettings("format=png"));
But i just get the error
"File may be corrupted, empty, or may contain a PNG image with a single dimension greater than 65,535 pixels."
When i do
using (Stream output = File.OpenWrite(Server.MapPath("~/resources/gfx/test.webp")))
using (Stream input = WebRequest.Create("http:///example.com/resources/gfx/unnamed.webp").GetResponse().GetResponseStream()) {
input.CopyTo(output);
}
ImageBuilder.Current.Build("~/resources/gfx/test.webp", "~/resources/gfx/photo3.png",
new ResizeSettings("format=png"));
It works fine, am i'm missing something here?
It's possible that 'output' has not been flushed to disk. .NET 4+ doesn't guarantee the file's actually written to disk just because you disposed the stream.
I assume you have the ImageResizer.Plugins.WebP plugin installed?

Opencv - create png image

As part of my project I wanted to send stream of images using websockets from embedded machine to client application and display them in img tag to achieve streaming.
Firstly I tried to send raw RGB data (752*480*3 - something about 1MB) but in the end I got some problems with encoding image to png in javascript based on my RGB image so I wanted to try to encode my data to PNG firstly and then sent it using websockets.
The thing is, I am having some problems with encoding my data to PNG using OpenCV library that is already used in the project.
Firstly, some code:
websocketBrokerStructure.matrix = cvEncodeImage(0, websocketBrokerStructure.bgrImageToSend, 0);
websocketBrokerStructure.imageDataLeft = websocketBrokerStructure.matrix->rows * websocketBrokerStructure.matrix->cols * websocketBrokerStructure.matrix->step;
websocketBrokerStructure.imageDataSent = 0;
but I am getting strange error during execution of the second line:
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_S_construct NULL not valid
and I am a bit confused why I am getting this error from my code.
Also I am wondering if I understand it right: after invoking cvEncodeImage (where bgrImage is IplImage* with 3 channels - BGR) I just need to iterate through data member of my CvMatto get all of the png encoded data?
The cvEncodeImage function takes as its first parameter the extension of the image you want to encode. You are passing 0, which is the same thing as NULL. That's why you are getting the message NULL not valid.
You should probably use this:
websocketBrokerStructure.matrix = cvEncodeImage(".png", websocketBrokerStructure.bgrImageToSend, 0);
You can check out the documentation of cvEncodeImage here.
You can check out some examples of cvEncodeImage, or its C++ brother imencode here: encode_decode_test.cpp. They also show some parameters you can pass to cvEncodeImage in case you want to adjust them.

ABCPDF Font Printing Layout - Machine Dependent

I am using ABCPDF to print a PDF file to a local printer via EMF file. I've based this very closely on ABC PDF's sample "ABCPDFView" project. My application worked fine on my Windows 7 and Windows XP dev boxes, but when I moved to a Windows 2003 test box, simple embedded fonts (like Times New Roman 12) rendered completely wrong (wrong spot, and short and squat, almost like the DPI's were crazily wrong).
Note that I've hardcoded the DPI to 240 here b/c I'm using a weird mainframe print driver that forces 240x240. I can discount that driver as the culprit as, if I save the EMF file locally during print, it shows the same layout problems. If I render to PNG or TIFF files, this looks just fine on all my servers using this same code (put .png in place of .emf). Finally, if I use the ABCPDFView project to manually add in a random text box to my PDF, that text also renders wrong in the EMF file. (Side note, if I print the PDF using Acrobat, the text renders just fine)
Update: I left out a useful point for anyone else having this problem. I can work around the problem by setting RenderTextAsText to "0" (see code below). This forces ABCPDF to render the text as polygons and makes the problem go away. This isn't a great solution though, as it greatly increases the size of my EMF files, and those polygons don't render nearly as cleanly in my final print document.
Anyone have any thoughts on the causes of this weird font problem?
private void DoPrintPage(object sender, PrintPageEventArgs e)
{
using (Graphics g = e.Graphics)
{
//... omitted code to determine the rect, used straight from ABC PDF sample
mDoc.Rendering.DotsPerInch = 240 ;
mDoc.Rendering.ColorSpace = "RGB";
mDoc.Rendering.BitsPerChannel = 8;
mDoc.SetInfo(0, "RenderTextAsText", "0");//the magic is right here
byte[] theData = mDoc.Rendering.GetData(".emf");
using (MemoryStream theStream = new MemoryStream(theData))
{
using (Metafile theEMF = new Metafile(theStream))
{
g.DrawImage(theEMF, theRect);
}
}
//... omitted code to move to the next page
}
Try upgrading to the new version of abcpdf 8, it has its own rendering engine based on Gecko and so you can bypass issues like this when abcpdf is using the inbuilt server version of IE for rendering.
I was originally RDPing in with 1920x1080 resolution, by switching to 1024x768 res for RDP, the problem went away. My main program runs as a service, and starting this service from an RDP session w/ 1024x768 fixes it.
I have an email out w/ ABC PDF to see if they can explain this and offer a more elegant solution, but for now this works.
Please note that this is ABC PDF 7, I have no idea if this issue applies to other versions.
Update: ABC PDF support confirmed that its possible the service is caching the display resolution from the person that started the process. They confirmed that they've seen some other weird issues with Remote Desktop and encouraged me to use this 1024x768 workaround and/or start the service remotely.

Loading the output from TOleContainer.SaveAsDocument

I have an existing database with blobs contain OLE compound files. I have a requirement to read these OLE compound files and open them in the Delphi 7 TOleContainer control.
Note that I don't have the source of the app that reads and write to the database. The database remains in active use, so my solution will be used on an ongoing basis, not just for a one-off data extraction.
TOleContainer has a SaveAsDocument method, and by experimentation I have found that, for a given file, this method produces OLE compound files which are identical to those created in the database when that file is added.
However, TOleContainer does NOT have a corresponding LoadFromDocument method. It has other Load* and Create* methods, but none seem capable or suitable for loading the output from SaveAsDocument.
The delphi 7 implementation of SaveAsDocument is this, from the OleCtnrs.pas module:
procedure TOleContainer.SaveAsDocument(const FileName: string);
var
TempStorage: IStorage;
PersistStorage: IPersistStorage;
begin
CheckObject;
if FModSinceSave then SaveObject;
FOleObject.QueryInterface(IPersistStorage, PersistStorage);
if PersistStorage <> nil then
begin
OleCheck(StgCreateDocFile(PWideChar(WideString(Filename)), STGM_READWRITE
or STGM_SHARE_EXCLUSIVE or STGM_CREATE, 0, TempStorage));
OleCheck(OleSave(PersistStorage, TempStorage, False));
PersistStorage.SaveCompleted(nil);
end;
end;
Please provide an implementation of LoadFromDocument which is capable of loading the output from SaveToDocument, and which I can use to patch OleCtnrs.pas. Or else point me to an existing solution.
Thanks!
You have to load the file by using TOleContainer.CreateObjectFromFile. Do not use TOleContainer.LoadFromStream/File, that only works with files that are saved with TOleContainer.SaveToStream/File. Files saved that way get a Delphi specific header containing a four byte code (BDOC) and size (and maybe something more).
According to the documentation for Delphi 2007 (should be the same for ), you can use 'TOleContainer.LoadFromStream'. From the Delphi 7 help file (emphasis mine):
Call LoadFromStream to load an OLE object from a stream. If OldStreamFormat is true, LoadFromStream loads OLE objects saved by a TOleContainer object as well as OLE objects saved using the current format; if OldStreamFormat is false, LoadFromStream will not load OLE objects saved by the library. If there's already an OLE object in the container, it is destroyed and any changes the user made to it are discarded.

Opening File paths with spaces in Delphi 5

(Using Delphi 5)
I am attempting to open a log file using the following code:
// The result of this is:
// C:\Program Files\MyProgram\whatever\..\Blah\logs\mylog.log
fileName := ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log';
// The file exists check passes
if (FileExists(fileName)) then
begin
logs := TStringList.Create();
// An exception is thrown here: 'unable to open file'
logs.LoadFromFile(fileName);
end;
If I relocate the log file to C:\mylog.log the code works perfectly. I'm thinking that the spaces in the file path are messing things up. Does anyone know if this is normal behavior for Delphi 5? If it is, is there a function to escape the space or transform the path into a windows 8.3 path?
I'm pretty sure that Delphi 5 handles spaces in filenames ok but it has been a very long time since I have used that specific version. Is the file currently open by another process? It also could be a permissions issue. Can you instead of loading it into a tStringList, try opening it with a tFileStream with the filemode set to "fmOpenRead or fmShareDenyNone".
fStm := tFileStream.Create( filename, fmOpenRead or fmShareDenyNone );
then load your tStringlist from the stream:
Logs.LoadFromStream ( fStm );
Are you sure its not the "..\" thats causing the problem rather than the spaces. Have you tried to see if it works at
c:\My\Path\nospaces\
If so and you are always using the ..\ path, maybe write a simple function to remove the last folder from your application path and create a full correct pathname.
It's odd that Delphi 5 would throw errors about this. I know of an issue with FileExists failing on files with an invalid last-modified-date (since it internally uses FileAge), but it's the opposite here. Instead of using "..\" I would consider risking the current path, and loading from a relative path: LoadFromFile('..\Something\Something.log'); especially for smaller applications, or by calling ExtractFilePath twice: ExtractFilePath(ExtractFilePath(Application.ExeName))
I'm pretty sure Delphi has always handled spaces so I doubt that is the issue.
You don't show the full path. Any chance it is really long? For example I could believe an issue with paths longer than 255 characters.
It's also a bad idea to put log files under Program Files. Often normal users are not given permission to write to anything under Program Files.
Delphi 5 can open files with spaces - that is certainly not the problem. To prove it, try copying it to c:\my log.log- it should open fine.
Is there any more information in the error message you receive? The most likely thing is that someone else (perhaps your own program) is still writing to the log.
The spaces are not a problem. While the '..' could be a problem in Delphi 5, mosts probably the file is locked by the process that writes to it. If you have control of it, make sure it opens the file with fmShareDenyWrite and not fmShareExclusive or fmShareCompat (which is the default).
Also, you can use:
fileName := ExpandFileName(ExtractFilePath(Application.ExeName) + '..\Blah\logs\mylog.log');
to obtain the absolute path from a relative path.
Also, as others have said, it is not good idea to write anything in Program Files. Regular users (that are not Administrators or Power Users) do not have rights to write there (although in Vista is will be virtualized, is is still not a good idea). Use the appropriate Application Data folder for the user (or all users). This folder can be obtained using:
SHGetFolderPath(0,folder,0,SHGFP_TYPE_CURRENT,#path[0])
where folder is either CSIDL_COMMON_APPDATA or CSIDL_LOCAL_APPDATA. See this delphi.about.com article for an example.
Simple :
// if log file = "C:\Program files\mylog.log"
// you'll get :
// »»»»» fileName = 'C:\Program files..\Blah\logs\mylog.log'
// if log file = "C:\mylog.log"
// you'll get :
// »»»»» fileName = 'C:..\Blah\logs\mylog.log'
Try this code instead, I'm pretty sure it will fit your needs :
fileName := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName))
+ '..\Blah\logs\mylog.log';
Regards,
Olivier
Delphi 5 has never had a problem opening files with spaces and I am still using it since it is uber stable and works great for older XP apps. You need to check your code closely.

Resources