Okay so i have an old 8-bit game that loads six .dat files for different size lightmaps.
Something along the lines of this:
const
MAX_LIGHT_COUNT = 5;
LFiles : array[0..MAX_LIGHT_COUNT] of string = (
'.\L00.dat',
'.\L01.dat',
'.\L02.dat',
'.\L03.dat',
'.\L04.dat',
'.\L05.dat'
);
type
TLights = record
Width : Integer;
Height : Integer;
PDark : PByte;
end;
var
LightArr: array[0..MAX_LIGHT_COUNT] of TLights;
Procedure InitializeLight();
var
PreviousSize: Integer;
i: Integer;
fHandle: Integer;
Width, Height: Integer;
begin
PreviousSize := 0;
for i := 0 to MAX_LIGHT_COUNT do
begin
if FileExists(LFiles[i]) then
begin
fHandle := FileOpen(LFiles[i], fmOpenRead or fmShareDenyNone);
FileRead(fHandle, Width, SizeOf(Integer));
FileRead(fHandle, Height, SizeOf(Integer));
LightArr[i].Width := Width;
LightArr[i].Height := Height;
LightArr[i].PDark := AllocMem(Width * Height + 8);
if PreviousSize < Width * Height then
FileRead(fHandle, LightArr[i].PDark^, Width * Height);
PreviousSize := Width * Height;
FileClose(fHandle);
end;
end;
end;
Now i need to create an editor for some new .dat files. I had a go basically reversing whats there and using FillChar to populate the array which just ended in a square instead of the lightmaps cirlce look which makes sense, think i am missing something very important with manipulating X, Y.
Something along the lines of:
PDark := #LightArr[i].PDark;
for Y := 0 to Height - 1 do
begin
for X := 0 to Width - 1 do
begin
// Do something with PDark
end;
end;
Which would then give me that circle look.
Download: LFiles.rar if necessary.
EDIT: Sorry Jerry if it come across that way, i wasn't expecting people to write the code for me just wanted to make sure i was going in the right direction and maybe get a little help and some other stuff to try out.
NOTE: In case people get confused the attached download isn't Source files its the .dat files the game loads. Uploaded in case people wanted to see what my binary files look like compared to theirs. Basically to compare output see if on right track ect.. I dunno lol.
This is what i tried but its missing some kind of manipulation code for the circle:
for i := 0 to MAX_LIGHT_COUNT do
begin
if FileExists(LFiles[i]) then
fHandle := FileOpen(LFiles[i], fmOpenWrite or fmShareDenyNone)
else fHandle := FileCreate(LFiles[i]);
if fHandle > 0 then
begin
Width := 196;
Height := 176;
FileWrite(fHandle, Width, SizeOf(Integer));
FileWrite(fHandle, Height, SizeOf(Integer));
LightArr[i].Width := Width;
LightArr[i].Height := Height;
LightArr[i].Fog := AllocMem(Width * Height + 8);
FillChar(LightArr[i].Fog^, LightArr[i].Width * LightArr[i].Height + 8, Width * Height);
FileWrite(fHandle, LightArr[i].Fog^, Width * Height);
FileClose(fHandle);
end;
end;
Thanks for reading.
Related
I cannot seem to figure out how to print multiple images in one page using Printer,
I want to display images side by side like this:
But the problem is the images always display in full page like this:
I have this code:
procedure TForm1.Button1Click(Sender: TObject);
var
MyRect: TRect;
scale: Double;
Bitmap : TBitmap;
i: integer;
begin
try
Bitmap := TBitmap.Create;
Bitmap.Width := Image1.Picture.Width;
Bitmap.Height := Image1.Picture.Height;
Bitmap.Canvas.Draw(0,0,Image1.Picture.Graphic);
if PrintDialog1.Execute then
begin
if Printer.Orientation = poPortrait then
scale := GetDeviceCaps(Printer.Handle, LOGPIXELSX) / Screen.PixelsPerInch
else
scale := GetDeviceCaps(Printer.Handle, LOGPIXELSY) / Screen.pixelsperinch;
Printer.BeginDoc;
MyRect := Rect(0,0, trunc(Bitmap.Width * scale), trunc(Bitmap.Height * scale));
Printer.Canvas.StretchDraw(MyRect, Bitmap);
Printer.EndDoc;
end;
finally
Bitmap.Free;
end;
end;
I want to the printer to printout images side by side, how can I accomplish that?
Can any one help me please?
Update:
procedure TForm1.Button1Click(Sender: TObject);
var
MyRect: TRect;
scale: Double;
Bitmap : TBitmap;
i, x, y, width, height, img_count: integer;
begin
Bitmap := TBitmap.Create;
x := 0;
y := 0;
img_count := 3;
try
begin
Bitmap.Width := Image1.Picture.Width;
Bitmap.Height := Image1.Picture.Height;
Bitmap.Canvas.Draw(0,0,Image1.Picture.Graphic);
if PrintDialog1.Execute then
begin
if Printer.Orientation = poPortrait then
scale := GetDeviceCaps(Printer.Handle, LOGPIXELSX) / Screen.PixelsPerInch
else
scale := GetDeviceCaps(Printer.Handle, LOGPIXELSY) / Screen.pixelsperinch;
Printer.BeginDoc;
for i := 1 to img_count do
begin
width := trunc(Bitmap.Width * scale / img_count);
height := trunc(Bitmap.Height * scale / img_count);
MyRect := Rect(x, y, width, height);
Printer.Canvas.StretchDraw(MyRect, Bitmap);
x := x + width;
end;
Printer.EndDoc;
end;
end;
finally
Bitmap.Free;
end;
end;
Now it displays the images like sticking at each other, an I want them to display with a little margin between them:
This is when I add margins:
This is without margins:
You have to halfway understand your code, then it will all become obvious. Canvas and Rect are literally just that - and if you max your rectangle out by scale you'll never put two pictures side by side. Cut the values in half and use understand the parameters of functions - I'll use more variables to make it more obvious why your approach is very easy to solve:
var
x, y, width, height: Integer;
...
begin
...
Printer.BeginDoc;
x:= 0; // Start on top left
y:= 0;
width:= trunc( Bitmap1.Width* scale/ 2 ); // Half of the size
height:= trunc( Bitmap1.Height* scale/ 2 )
Printer.Canvas.StretchDraw( Rect( x, y, width, height ), Bitmap1 );
x:= width; // Continue after picture on the right side
width:= trunc( Bitmap2.Width* scale/ 2 ); // Again half of the size
height:= trunc( Bitmap2.Height* scale/ 2 )
Printer.Canvas.StretchDraw( Rect( x, y, width, height ), Bitmap2 );
Printer.EndDoc;
This example presumes Bitmap1 and Bitmap2 have similar dimensions.
I'm trying to do the following:
bmp := TBitmap.Create;
bmp.Width := FWidth;
bmp.Height := FHeight;
for y := 0 to FHeight - 1 do
begin
sl := bmp.ScanLine[y];
for x := 0 to FWidth - 1 do
begin
//draw to the scanline, one pixel at a time
end;
end;
//display the image
bmp.Free;
Unfortunately, what I end up with is an image that's completely white, except for the bottom line, which is colored appropriately. A bit of debugging shows that each time I access the ScanLine property, it's calling TBitmap.FreeImage, and going into the if (FHandle <> 0) and (FHandle <> FDIBHandle) then block, which resets the whole image, so only the changes to the last line actually take.
In every demo I've seen so far using TBitmap.ScanLine, they start out by loading an image. (Apparently this sets up various handles correctly so that this doesn't end up happening?) But I'm not trying to load an image and work on it; I'm trying to capture image data from a camera.
How can I set up the bitmap so that I can draw to the scanlines without having to load an image first?
You should set the PixelFormat explicitly before starting to draw. For instance,
procedure TForm1.FormPaint(Sender: TObject);
var
bm: TBitmap;
y: Integer;
sl: PRGBQuad;
x: Integer;
begin
bm := TBitmap.Create;
try
bm.SetSize(1024, 1024);
bm.PixelFormat := pf32bit;
for y := 0 to bm.Height - 1 do
begin
sl := bm.ScanLine[y];
for x := 0 to bm.Width - 1 do
begin
sl.rgbBlue := 255 * x div bm.Width;
sl.rgbRed := 255 * y div bm.Height;
sl.rgbGreen := 255 * x div bm.Width;
inc(sl);
end;
end;
Canvas.Draw(0, 0, bm);
finally
bm.Free;
end;
end;
I have a code, that draws the message str directly to center of the screen without a visible window.
Why using this code first works OK, but after dozens of calls, it gives Out of system resources.
It seems to free BM ok, and I don't see that it allocates other resources at all.
procedure ttsplash.UpdateSplash(const Str: string);
var
R: TRect;
P: TPoint;
S: TPoint;
bm: TBitmap;
bf: TBlendFunction;
EXSTYLE: DWORD;
x, y: integer;
pixel: PRGBQuad;
TextRed,
TextGreen,
TextBlue: byte;
begin
if str='' then exit;
EXSTYLE := GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, EXSTYLE or $80000);
R := ClientRect;
bm := TBitmap.Create;
try
bm.PixelFormat := pf32bit;
// bm.SetSize(ClientWidth, ClientHeight);
bm.Width := clientwidth;
bm.height := clientheight;
bm.Canvas.Brush.Color := clBlack;
bm.Canvas.FillRect(ClientRect);
bm.Canvas.Font.Assign(Self.Font);
bm.Canvas.Font.Color := clWhite;
DrawText(bm.Canvas.Handle, PChar(Str), Length(Str), R,
DT_SINGLELINE or DT_VCENTER or DT_CENTER or DT_WORD_ELLIPSIS);
TextRed := GetRValue(ColorToRGB(Font.Color));
TextGreen := GetGValue(ColorToRGB(Font.Color));
TextBlue := GetBValue(ColorToRGB(Font.Color));
for y := 0 to bm.Height - 1 do
begin
pixel := bm.ScanLine[y];
x := 0;
while x < bm.Width do
begin
with pixel^ do
begin
rgbReserved := (rgbRed + rgbGreen + rgbBlue) div 3;
rgbBlue := TextBlue * rgbReserved div 255;
rgbGreen := TextGreen * rgbReserved div 255;
rgbRed := TextRed * rgbReserved div 255;
end;
inc(pixel);
inc(x);
end;
end;
P := Point(0, 0);
S := Point(bm.Width, bm.Height);
bf.BlendOp := AC_SRC_OVER;
bf.BlendFlags := 0;
bf.SourceConstantAlpha := 255;
bf.AlphaFormat := AC_SRC_ALPHA;
UpdateLayeredWindow(Handle, 0, nil, #S, bm.Canvas.Handle, #P, 0, #bf, ULW_ALPHA)
finally
bm.Free;
end;
end;
How to debug this.
Enable debug DCUs in your project options, disable optimizations.
When you get out of resources error, hit "Break".
Inspect call stack :
The problem happens in CopyBitmap when calling GDICheck -> double click GDICheck to go there.
Put a breakpoint. Run the program - count how many times it takes before the error shows up and break just before you expect the error.
Have a look around for anything that might be odd. A good place to start is the bitmap itself. Your first clue should be that each time you call this method your text is creeping away up into the corner of your invisible form.
Let's check the bitmap header and see what's going on :
Looks like your bitmap dimensions are negative. I wonder how that happened. In fact, if you watch each time this is called, your bitmap is shrinking each time. In fact, it is shrinking by 16px in width and 38px in height - the size of the window frame.
Each time you are calling UpdateLayeredWindow you are resizing your form (its outside dimension) to be the size of the client area - the size without the window frame. Your new window gets a new frame and the client area shrinks.
Eventually there is nothing left and you are trying to make a bitmap with negative dimensions. You should therefore take into account the frame size when building your bitmap. Use the form width and height rather than the client size :
bm.Width := Width;
bm.height := Height;
Also, when making API calls, please get into the habit of checking the return values for errors, as described in the documentation for the function in question. If you are not checking for errors you are asking for problems.
Without your feedback this remains a guess, but passing a device context with the size of your form's client area, you reduce the size of your form with each call to UpdateLayeredWindow. When, eventually, you request a negative value for the bitmap dimensions, CreateCompatibleBitmap in the code path returns an error.
How to get screenshot of browser in DCEF3?
I create browser like this without VCL. The TakePicture method will only work if
No debugger is used
If ShowWindow is used
var
info: TCefWindowInfo;
Settings: TCefBrowserSettings;
begin
FillChar(info, SizeOf(info), 0);
info.width := width;
info.height := height;
FillChar(Settings, SizeOf(TCefBrowserSettings), 0);
Settings.Size := SizeOf(TCefBrowserSettings);
GetSettings(Settings);
CefBrowserHostCreateBrowser(#info, FHandler, FDefaultUrl, #settings, nil);
end;
procedure TakePicture(const Browser: ICefBrowser; Height, Width: Integer);
var
DC: HDC;
Bmp : TBitmap;
Handle : HWND;
Rect : trect;
BarHeight : integer;
BarLeft : integer;
begin
Bmp := TBitmap.Create;
Bmp.PixelFormat := pf32bit;
Handle := Browser.Host.WindowHandle;
ShowWindow(handle, SW_RESTORE); // will work only if this is used otherwise black image!
BarLeft := GetSystemMetrics(SM_CXFRAME);
BarHeight := GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME);
GetWindowRect(Handle, Rect);
DC := GetDC(Handle);
Bmp.Width := Rect.Right - Rect.Left;
Bmp.Height := (Rect.Bottom - Rect.Top);
BitBlt(Bmp.Canvas.Handle, 0, 0, Bmp.Width, Bmp.Height, DC, -BarLeft, -BarHeight, SRCCOPY);
ReleaseDC(Handle, DC);
Bmp.SaveToFile('c:\test.bmp');
Bmp.Free;
end;
This is basically off-screen rendering. In the demos folder of DCEF3 you'll find a project 'offscreen'. The code you're looking for is in the OnPaint event of TChromiumOSR. It renders to a TBitmap32, but any bitmap could be made to work. Notice that it has been optimized to only paint the so-called "dirty" areas (those that have changed since last painting), but if you're making a screenshot, that's not what you want. In my check-out of the repository there's a line commented out showing the naive case of just painting everything:
SomeBitmap.SetSize(width, height);
Move(buffer^, SomeBitmap32.Bits^, width * height * 4);
It's my best guess that the magic number 4 represents 4 bytes (32-bits).
I warmly recommend using Graphics32 but it you have to use a regular TBitmap, I'll leave it up to you to work out how to turn the array of bits into pixels. Be warmed it will probably be a lot slower.
What is the fastest way of getting the area of any arbitrary Windows region?
I know I can enumerate all points of bounding rectangle and call the PtInRegion() function but it seems not very fast. Maybe you know some faster way?
When you call GetRegionData, you'll get a list of non-overlapping rectangles that make up the region. Add up their areas, something like this:
function GetRegionArea(rgn: HRgn): Cardinal;
var
x: DWord;
Data: PRgnData;
Header: PRgnDataHeader;
Rects: PRect;
Width, Height: Integer;
i: Integer;
begin
x := GetRegionData(rgn, 0, nil);
Win32Check(x <> 0);
GetMem(Data, x);
try
x := GetRegionData(rgn, x, Data);
Win32Check(x <> 0);
Header := PRgnDataHeader(Data);
Assert(Header.iType = rdh_Rectangles);
Assert(Header.dwSize = SizeOf(Header^));
Rects := PRect(Cardinal(Header) + Header.dwSize);
// equivalent: Rects := PRect(#Data.Buffer);
Result := 0;
for i := 0 to Pred(Header.nCount) do begin
Width := Rects.Right - Rects.Left;
Height := Rects.Bottom - Rects.Top;
Inc(Result, Width * Height);
Inc(Rects);
end;
finally
FreeMem(Data);
end;
end;