I want to create large Bitmap with code
LargeBmp := TBitmap.Create;
try
LargeBmp.Width := 1000; // fine
LargeBmp.Height := 15000; // XP - EOutOfResources, Not enough memory, Win 7 - works fine
...
finally
FreeAndNil(LargeBmp);
end;
This code raises an EOutOfResources exception with message "Not enough memory" on Windows XP but works fine in Windows 7.
What is wrong? Why Not enough memory? It's only 60 MB.
Set the pixel format like this:
LargeBmp.PixelFormat := pf24Bit;
I had the same problem several times and that always solved it.
As was discussed already, if you don't set the pixel format Windows will see it as a device-dependent bitmap. When you set the pixel format you create a DIB (device-independent bitmap). This means it's independent of the display device (graphics card).
I've had this same problem, and wanted to point out that pf24bit is not the only option. In the Graphics unit, you also have:
TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);
For a project I'm working on, I found the 8 bit option worked the best for what I needed, since I had a very large bitmap (high resolution), but limited colors (I was creating the whole bitmap from some simple code).
So try out a few others, other than just pf24bit, to find what's optimal for your production environment. I saved quite a bit of internal memory with the pf8bit option.
Created bitmaps (by default) is stored in some buffer. That buffer's size depends on videodriver, os and God knows what else.
This buffer can be pretty small (about 20-25mb) and if you will try to create more, it will fail.
To avoid this try to create DIB instead of TBitmap, or try to change Pixelformat to pf24bit. This will tell system to create Bitmap in user's memory instead of GDI buffer.
Now, why it not fails in win7 you ask? Ok, probably cause there is no GDI, but GDI+ and Direct2D uses in win 7 instead. Maybe other driver's version, dunno.
Related
I'm using the SharpDX Toolkit, and I'm trying to create a Texture2D programmatically, so I can manually specify all the pixel values. And I'm not sure what pixel format to create it with.
SharpDX doesn't even document the toolkit's PixelFormat type (they have documentation for another PixelFormat class but it's for WIC, not the toolkit). I did find the DirectX enum it wraps, DXGI_FORMAT, but its documentation doesn't give any useful guidance on how I would choose a format.
I'm used to plain old 32-bit bitmap formats with 8 bits per color channel plus 8-bit alpha, which is plenty good enough for me. So I'm guessing the simplest choices will be R8G8B8A8 or B8G8R8A8. Does it matter which I choose? Will they both be fully supported on all hardware?
And even once I've chosen one of those, I then need to further specify whether it's SInt, SNorm, Typeless, UInt, UNorm, or UNormSRgb. I don't need the sRGB colorspace. I don't understand what Typeless is supposed to be for. UInt seems like the simplest -- just a plain old unsigned byte -- but it turns out it doesn't work; I don't get an error, but my texture won't draw anything to the screen. UNorm works, but there's nothing in the documentation that explains why UInt doesn't. So now I'm paranoid that UNorm might not work on some other video card.
Here's the code I've got, if anyone wants to see it. Download the SharpDX full package, open the SharpDXToolkitSamples project, go to the SpriteBatchAndFont.WinRTXaml project, open the SpriteBatchAndFontGame class, and add code where indicated:
// Add new field to the class:
private Texture2D _newTexture;
// Add at the end of the LoadContent method:
_newTexture = Texture2D.New(GraphicsDevice, 8, 8, PixelFormat.R8G8B8A8.UNorm);
var colorData = new Color[_newTexture.Width*_newTexture.Height];
_newTexture.GetData(colorData);
for (var i = 0; i < colorData.Length; ++i)
colorData[i] = (i%3 == 0) ? Color.Red : Color.Transparent;
_newTexture.SetData(colorData);
// Add inside the Draw method, just before the call to spriteBatch.End():
spriteBatch.Draw(_newTexture, new Vector2(0, 0), Color.White);
This draws a small rectangle with diagonal lines in the top left of the screen. It works on the laptop I'm testing it on, but I have no idea how to know whether that means it's going to work everywhere, nor do I have any idea whether it's going to be the most performant.
What pixel format should I use to make sure my app will work on all hardware, and to get the best performance?
The formats in the SharpDX Toolkit map to the underlying DirectX/DXGI formats, so you can, as usual with Microsoft products, get your info from the MSDN:
DXGI_FORMAT enumeration (Windows)
32-bit-textures are a common choice for most texture scenarios and have a good performance on older hardware. UNorm means, as already answered in the comments, "in the range of 0.0 .. 1.0" and is, again, a common way to access color data in textures.
If you look at the Hardware Support for Direct3D 10Level9 Formats (Windows) page you will see, that DXGI_FORMAT_R8G8B8A8_UNORM as well as DXGI_FORMAT_B8G8R8A8_UNORM are supported on DirectX 9 hardware. You will not run into compatibility-problems with both of them.
Performance is up to how your Device is initialized (RGBA/BGRA?) and what hardware (=supported DX feature level) and OS you are running your software on. You will have to run your own tests to find it out (though in case of these common and similar formats the difference should be a single digit percentage at most).
It is important for a MIDI player to playback the notes as precisely as possible. I never quite succeeded in that, always blaming the timer (see a previous thread: How to prevent hints interrupting a timer). Recently I acquired ProDelphi and started measuring what exactly consumed that much time. The result was quite surprising, see the example code below.
procedure TClip_View.doMove (Sender: TObject; note, time, dure, max_time: Int32);
var x: Int32;
begin
{$IFDEF PROFILE}Profint.ProfStop; Try; Profint.ProfEnter(#self,1572 or $58B20000); {$ENDIF}
Image.Picture.Bitmap.Canvas.Pen.Mode := pmNot;
Image.Picture.Bitmap.Canvas.MoveTo (FPPos, 0);
Image.Picture.Bitmap.Canvas.LineTo (FPPos, Image.Height);
x := time * GPF.PpM div MIDI_Resolution;
Image.Picture.Bitmap.Canvas.Pen.Mode := pmNot;
Image.Picture.Bitmap.Canvas.MoveTo (x, 0);
Image.Picture.Bitmap.Canvas.LineTo (x, Image.Height);
FPPos := x;
// Bevel.Left := time * GPF.PpM div MIDI_Resolution;
{$IFDEF PROFILE}finally; Profint.ProfExit(1572); end;{$ENDIF}
end; // doMove //
The measurements are (without debug code on an Intel i7-920, 2,7Ghz):
95 microseconds for the code as shown
5.609 milliseconds when all is commented out except for the now commented out statement (Bevel.Left :=)
0.056 microseconds when all code is replaced by x := time * GPF.PpM div MIDI_Resolution;
Just moving around a Bevel costs 60 times as much CPU as just drawing on a Canvas. That surprised me. The results of measurement 1 are very audible (there is more going on than just this), but 2 and 3 not. I need some form of feedback to the user as what the player now is processing, some sort of line over a piano roll is the accepted way. In my never ending quest of reducing CPU cycles in the timed-event loop I have some questions:
Why does moving around a bevel cost that much time?
Is there a way to reduce more CPU cycles than in drawing on a bitmap?
Is there a way to reduce the flicker when drawing?
You won't be able to change the world, nor VCL nor Windows. I suspect you are asking to much to those...
IMHO you should better change a little bit your architecture:
Sound processing shall be in one (or more) separated thread(s), and should not be at all linked to the UI (e.g. do not send GDI messages from it);
UI refresh shall be made using a timer with a 500 ms resolution (half a second refresh sounds reactive enough), not every time there is a change.
That is, the sequencer won't refresh the UI, but the UI will ask periodically the sequencer what is its current status. This will be IMHO much smoother.
To answer your exact questions:
"Moving a bevel" is in fact sending several GDI messages, and the rendering will be made by the GDI stack (gdi32.dll) using a temporary bitmap;
Try to use a smaller bitmap, or try using a Direct X buffer mapping;
Try DoubleBuffered := true on your TForm.OnCreate event, or use a dedicated component (TPaintBox) with a global bitmap for the whole component content, with something like that for the message WM_ERASEBKGND.
Some code:
procedure TMyPaintBox.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
Message.Result := 1; // no erasing is necessary after this method call
end;
I have the feeling that your bitmap buffering is wrong. When you move your clip it shouldn't have to be redrawn at all. you could try with This clip component structure:
TMidiClip = Class(TControl)
Private
FBuffer: TBitmap;
FGridPos: TPoint;
FHasToRepaint: Boolean;
Public
Procedure Paint; Override; // you only draw the bitmap on the control canvas
Procedure Refresh; // you recompute the FBuffer.canvas
End;
When you change some properties such as "clip tick length" you set "FHasToRepaint" to true but not when changing "FGridPos" (position on the grid). So most of the time, in your Paint event, you only have a copy of your FBuffer.
Actually, this is very dependent on the design of your grid and its children (clips).
I might be wrong but it seems that your design is not enough decomposed in Controls: the master grid should be a TControl, a clip should be a TControl, even the events on a clip should be some TControls...You can only define a strongly optimized bitmap-buffering system by this way (aka "double-buffering").
About the Timer: you should use a musical clock which processes per audio sample otherwise you can't have a good enough resolution. Such a clock can be implemented using the "Windows Audio driver" (mmsystem.pas) or an "Asio" driver (you have an interface in BASS, The Delphi Asio Vst project, for example).
By far the best way to tackle this is to stop the GUI message queue interfering with your MIDI player. Put the MIDI player on a background thread so that it can do its work without interruption from the main thread. Naturally this relies on you running on a machine with more than a single processor but it's not unreasonable to take that for granted nowadays.
Judging from your comments it looks like your audio processing is being blocked by the UI thread. Don't let that happen and your audio problems will disappear. If you are using something like TThread.Synchronize to fire VCL events from the audio thread then that will block on the UI thread. Use an asynchronous communication method instead.
Your proposed alternative of speeding up the VCL is not really viable. You can't modify the VCL and even if you could, the bottleneck could easily be the underlying Windows code.
I get an "EOutofresources - Not enough storage" when I try to work with BMP files when I try to set BMP.Height or BMP.Width. Imediatelly after these instructions, the stack trace is (in this order):
ntdll.dll.RtlLeaveCriticalSection, kernel32.dll.FileTimeToDosDateTime, GDI32.dll.GdiReleaseDC, GDI32.dll.PatBlt, kernel32.dll.ReadFile or like this:
|7E429130|user32.dll GetParent
|7C90FF2D|ntdll.dll RtlGetNtGlobalFlags
|77F15A00|GDI32.dll GdiReleaseDC
|7C83069E|kernel32.dll FileTimeToDosDateTime
|7C9010E0|ntdll.dll RtlLeaveCriticalSection
| |my function (where I set BMP.Height or BMP.Width)
At a moment I was sure that it has to do something with memory fragmentation - the system had enough free ram to process my image BUT the memory was fragmented so there was no block large enough to hold my image. But then I have seen it happening once 11 seconds after Windows start up. My program cycled through the loop where I process the images ONLY once! So, this could not be related to RAM fragmentation.
A different situation (but still related to drawing) when I got this error is below:
|77F16A7E|GDI32.dll IntersectClipRect
|77F16FE5|GDI32.dll BitBlt
|7E429011|user32.dll OffsetRect
|7E42A97D|user32.dll CallWindowProcA
|7E42A993|user32.dll CallWindowProcA
|7C9010E0|ntdll.dll RtlLeaveCriticalSection
|7E4196C2|user32.dll DispatchMessageA
|7E4196B8|user32.dll DispatchMessageA
|0058A2E1|UTest.exe UTest.dpr
|7C90DCB8|ntdll.dll ZwSetInformationThread
I think there is always a 'RtlLeaveCriticalSection' call in the stack trace after BMP.Height.
There is this post pointing to a possible solution by editing a Windows registry key. However, the post says that it applies only to Win XP. While my error appears also on Win 7.
I see many similar posts (some of them are close connected to saving a file to disk) but until nobody came back to report that he fixed the error.
Update:
As you requested, this is the code where the error appears:
procedure TMyBitmap.SetLargeSize(iWidth, iHeight: Integer);
CONST ctBytesPerPixel= 3;
begin
{ Protect agains huge/empty images }
if iWidth< 1 then iWidth:= 1 else
if iWidth> 32768 then iWidth:= 32768;
if iHeight< 1 then iHeight:= 1 else
if iHeight> 32768 then iHeight:= 32768;
{ Set image type }
if iWidth * iHeight * ctBytesPerPixel > 9000000 {~9MB}
then HandleType:= bmDIB { Pros and cons: -no hardware acceleration, +supports larger images }
else HandleType:= bmDDB;
{ Total size is higher than 1GB? }
if (iWidth* iHeight* ctBytesPerPixel) > 1*GB then
begin
Width := 8000; { Set a smaller size }
Height := 8000; { And rise an error }
RAISE Exception.Create('Image is too large.');
end;
{ Set size }
Width := iWidth; <----------------- HERE
Height:= iHeight;
end;
From my experiment, the maximum bitmap size depends on:
The OS version (e.g. XP seems to allow smaller bitmap resources than Seven);
The OS edition (64 bit OS allows bigger resource allocation than 32 bit OS);
The current RAM installed (and free);
The number of bitmaps already allocated (since those are shared resources).
So you can not be sure that a bitmap allocation will be successful, when you start working with huge data (more than an on-screen bitmap resolution).
Here are some potential solutions (I've used some of them):
Allocate not a bitmap resource, but a plain memory block to work with, then use direct Win32 BitBlt API to draw it - but you'll have to write some dedicated process functions (or use some third party libraries), and on 32 bit OS, IMHO VirtualAlloc API (the one called by FastMM4 for big blocks of memory) won't be able to allocate more than 1 GB of contiguous memory;
Enhancement of the previous version: either use a 64 bit process to handle huge RAM block (welcome XE2 compiler), or use a file for temporary storage, then memory-map its content for processing (it is how PhotoShop or other handle huge memory) - if you have enough RAM, using a temporary file won't be necessary slower (no data will be written on disk);
Tile you big pictures into smaller pictures - the JPEG library is able to render only a portion of the picture, and it will fit easily into a bitmap resource;
In all cases, prevent any duplicated bitmap resource (since all bitmap resource are shared): for instance, if you are reading from a bitmap, copy its content into a temporary memory block or file, release its resource, then allocate your destination bitmap;
About performance, make it right, then make it fast - do not include "tricks" too early in your implementation (your customer may accept waiting some seconds, but won't accept a global failure).
There is no perfect solution (my preference is to use smaller pictures, because it has the advantage of being easily multi-threaded for the process, so may speed up a lot with new CPU), and be aware that resource allocation may work on your brand new 64 bit Windows Seven PC, but fail on the 32 bit XP computer of your customer.
I want to open some relative big files (jpg, gif, bmp) using as little RAM as possible.
Inside my program I need all open files converted to BMP so I can process them. However the conversion from JPG to BMP takes 27.1MB of RAM if I use the classic conversion code:
function ConvertJPG2BMP(FullFileName: string; BMP: TBitmap);
VAR JPG: TJpegImage;
begin
JPG:= TJpegImage.Create;
TRY
TRY
JPG.LoadFromFile(FullFileName);
BMP.Assign(JPG);
EXCEPT
END;
FINALLY
FreeAndNil(JPG);
end;
end;
because it uses two images (a jpeg that is transferred to a bitmap then).
--
However, if I use a TPicture to load the file, I use only 7.1MB of RAM. But in this case the TPicture.Bitmap is empty and I need a valid TBitmap object.
Is there any way to load images from disk while keeping the mem footprint small?
--
(Test file: 1.JPG 2.74MB 3264x1840 pix)
Back of the envelope calculation gives 6 mega pixels. Assuming 32 bit colour this takes you to 24MB.
You aren't going to do any better than your current code.
The memory usage does not come from the JPEG library, but in the way you use it.
If you convert a JPEG into a TBitmap, it will create a bitmap resource, then uncompress the JPEG into the bitmap memory buffer.
You can paint directly from the JPEG content into the screen. Depending on the JPEG implementation, it will use (or not) a temporary TBitmap.
You are not tied to the JPEG unit supplied by Borland.
For instance, you may try calling directly the StretchDIBits() windows API from the uncompressed memory buffer, as such (this code is extracted from our SSE JPEG decoder):
procedure TJpegDecode.DrawTo(Canvas: TCanvas; X, Y: integer);
var BMI: TBitmapInfo;
begin
if #self=nil then
exit;
ToBMI(BMI);
StretchDIBits(Canvas.Handle,X,Y,width,height,0,0,width,height,pRGB,
BMI,DIB_RGB_COLORS,SrcCopy);
end;
Creating a huge bitmap is sometimes not possible (at least under Windows XP), because it uses shared GDI resources, whereas using plain RAM and StretchDIBits will always work, even for huge content. You can create a memory mapped file to handle the binary content, but just allocating the memory at once would suffice (and Windows will use hard drive only if short of RAM). With today's PCs, you should have enough RAM available even for big pictures. (17 MB is not a big deal, even for your 3264x1840 pix).
Then, from this global uncompressed memory buffer containing raw pixel triplets, you can use
a smaller bitmap corresponding to a region of the picture, then work on the region using StretchDIBits(aBitmap.Handle,.... It will use less GDI resource.
You could also rely for instance on GDI+ drawing, which will draw it without any temporary bitmap. See e.g. this OpenSource unit. From our testing, it's very fast and can be used without any TBitmap. You could also ask only for a region of the whole picture, and draw it using GDI+ on your bitmap canvas: this will use less RAM. And your exe will be a bit smaller than the default JPEG unit. And you'll be able to display and save not only JPEG, but GIF and TIFF formats.
If you want to minimize even further the memory usage, you'll have to call directly the JPEG library, at lowest-level. It's able to uncompress only a region of the JPEG. So you would be able to minimize used RAM. You may try using the IJL library with Delphi, a bit old, but still working.
I don't have any background in programming and this is my first shot. I wrote a Delphi program that is supposed to print on a result sheet. I work in an institute and we have to establish hundreds of result sheets every 2 months. It's really difficult to do that and different handwriting is also an important issue. I have this code:
if PrintDialog.Execute() then
begin
with MyPrinter do
begin
MyPrinter.BeginDoc();//Start Printing
//Prints First Name
MyPrinter.Canvas.TextOut(FirstNameX,FirstNameY,EditFirstName.Text);
//Prints Last Name
MyPrinter.Canvas.TextOut(LastNameX,LastNameY,EditLastName.Text);
//Prints Level
MyPrinter.Canvas.TextOut(LevelX,LevelY,EditLevel.Text);
//Prints Date
MyPrinter.Canvas.TextOut(DateX,DateY,MEditDate.Text);
//Prints Student Number
MyPrinter.Canvas.TextOut(StdNumX,StdNumY,EditStdnumber.Text);
....
MyPrinter.EndDoc();//End Printing
end;
end;
I can't get the right coordinates to print properly. Am I missing something? How can I set the right coordinates? You know TPrinter uses pixels to get the coordinates but papers are measured in inches or centimeters.
I'm really confused. I appreciate any help.
#Milad I recommend you use a report component like Quick Report (comes with the oldies versions of delphi and is very easy to use) or FreeReport (it's free) to make your task easier.
for more info check theses links
QuickReports Tutorial
QuickReports Documentation, Knowledge Base and FAQ
if you want to print directly from delphi without components here I leave a couple of links that can help.
Printing Directly from Delphi
Delphi - Printing via the TPrinter Canvas
While using a reporting tool is a good idea, if you really do want to do it yourself, you can. I do this for printing to custom paper in our licensing application. The key is to know the size of your paper, and work it out from that using the printer PageHeight and PageWidth properties. For A4 paper I chose to use 297mm by 210mm, and from that I was able to calculate where I wanted things to be. The calculation is done thus:
nStartPos := 210-141;
nUserColX := muldiv(localPrinter.PageWidth, 187, 297);
nUserColY := muldiv(localPrinter.PageHeight, nStartPos, 210);
The nStartPos variable is done to start on a particular place, and then nUserColY is used to move down a line at a time as here:
nUserColY := nUserColY - localPrinter.canvas.font.height - (localPrinter.canvas.font.height div 8);
This then allows more than one line at a time to fit nicely.
This is not complete, but should be a good start for custom printing.