PDA

View Full Version : Glow, Blur in 2d with no opengl or d3d ? possible ?



arthurprs
06-07-2007, 04:17 PM
The titles explains itself :?

sorry my bad english

JernejL
06-07-2007, 04:24 PM
i have some code to blur a image somewhere if you need that.

WILL
06-07-2007, 04:24 PM
Not really. :roll: What platform, compiler, language are you using?

Anything is possible, only the exact implementation can be impossible.

btw, Welcome to PGD. :)

arthurprs
06-07-2007, 05:11 PM
Not really. :roll: What platform, compiler, language are you using?

Anything is possible, only the exact implementation can be impossible.

btw, Welcome to PGD. :)

Opz sorry :?

Windows,
Delphi 2006 win32,
Object pascal...

arthurprs
06-07-2007, 05:11 PM
i have some code to blur a image somewhere if you need that.

Ohh it will be great ^^

JernejL
06-07-2007, 05:43 PM
{

Gaussian Blur in Delphi

From: ullrich@math.okstate.edu
You can do it like so. In informal testing it appears to take roughly twice as much time as Adobe Photoshop takes to do the same thing, which seems pretty OK to me - there are a lot of things you could do to speed it up.
The gaussian kernel exp(-(x^2 + y^2)) is of the form f(x)*g(y), which means that you can perform a two-dimensional convolution by doing a sequence of one-dimensional convolutions - first you convolve each row and then each column. This is much faster (an N^2 becomes an N*2). Any convolution requires some temporary storage - below the BlurRow routine allocates and frees the memory, meaning that it gets allocated and freed once for each row. Probably changing this would speed it up some, it's not entirely clear how much.
The kernel "size" is limited to 200 entries. In fact if you use radius anything like that large it will take forever - you want to try this with a radius = 3 or 5 or something. For a kernel with that many entries a straight convolution is the thing to do, while when the kernel gets much larger Fourier transform techniques will be better (I couldn't say what the actual cutoff is.)
One comment that needs to be made is that a gaussian blur has the magical property that you can blur each row one by one and then blur each column - this is much faster than an actual 2-d convolution.
Anyway, you can do this:
}

unit GBlur2;

interface

uses Windows, Graphics;

type
PRGBTriple = ^TRGBTriple;
TRGBTriple = packed record
b: byte; //easier to type than rgbtBlue...
g: byte;
r: byte;
end;

PRow = ^TRow;
TRow = array[0..1000000] of TRGBTriple;

PPRows = ^TPRows;
TPRows = array[0..1000000] of PRow;


const MaxKernelSize = 100;

type

TKernelSize = 1..MaxKernelSize;

TKernel = record
Size: TKernelSize;
Weights: array[-MaxKernelSize..MaxKernelSize] of single;
end;
//the idea is that when using a TKernel you ignore the Weights
//except for Weights in the range -Size..Size.

procedure GBlur(theBitmap: TBitmap; radius: single);

implementation

uses SysUtils;

procedure MakeGaussianKernel(var K: TKernel; radius: single;
MaxData, DataGranularity: single);
//makes K into a gaussian kernel with standard deviation = radius.
//for the current application you set MaxData = 255,
//DataGranularity = 1. Now the procedure sets the value of
//K.Size so that when we use K we will ignore the Weights
//that are so small they can't possibly matter. (Small Size
//is good because the execution time is going to be
//propertional to K.Size.)
var j: integer; temp, delta: single; KernelSize: TKernelSize;
begin
for j:= Low(K.Weights) to High(K.Weights) do
begin
temp:= j/radius;
K.Weights[j]:= exp(- temp*temp/2);
end;

//now divide by constant so sum(Weights) = 1:

temp:= 0;
for j:= Low(K.Weights) to High(K.Weights) do
temp:= temp + K.Weights[j];
for j:= Low(K.Weights) to High(K.Weights) do
K.Weights[j]:= K.Weights[j] / temp;


//now discard (or rather mark as ignorable by setting Size)
//the entries that are too small to matter -
//this is important, otherwise a blur with a small radius
//will take as long as with a large radius...
KernelSize:= MaxKernelSize;
delta:= DataGranularity / (2*MaxData);
temp:= 0;
while &#40;temp <delta> 1&#41; do
begin
temp&#58;= temp + 2 * K.Weights&#91;KernelSize&#93;;
dec&#40;KernelSize&#41;;
end;

K.Size&#58;= KernelSize;

//now just to be correct go back and jiggle again so the
//sum of the entries we'll be using is exactly 1&#58;

temp&#58;= 0;
for j&#58;= -K.Size to K.Size do
temp&#58;= temp + K.Weights&#91;j&#93;;
for j&#58;= -K.Size to K.Size do
K.Weights&#91;j&#93;&#58;= K.Weights&#91;j&#93; / temp;

end;

function TrimInt&#40;Lower, Upper, theInteger&#58; integer&#41;&#58; integer;
begin
if &#40;theInteger <Upper>= Lower&#41; then
result&#58;= theInteger
else
if theInteger > Upper then
result&#58;= Upper
else
result&#58;= Lower;
end;

function TrimReal&#40;Lower, Upper&#58; integer; x&#58; single&#41;&#58; integer;
begin
if &#40;x <upper>= lower&#41; then
result&#58;= trunc&#40;x&#41;
else
if x > Upper then
result&#58;= Upper
else
result&#58;= Lower;
end;

procedure BlurRow&#40;var theRow&#58; array of TRGBTriple; K&#58; TKernel; P&#58; PRow&#41;;
var j, n, LocalRow&#58; integer; tr, tg, tb&#58; single; //tempRed, etc
w&#58; single;
begin

for j&#58;= 0 to High&#40;theRow&#41; do
begin
tb&#58;= 0;
tg&#58;= 0;
tr&#58;= 0;
for n&#58;= -K.Size to K.Size do
begin
w&#58;= K.Weights&#91;n&#93;;

//the TrimInt keeps us from running off the edge of the row...
with theRow&#91;TrimInt&#40;0, High&#40;theRow&#41;, j - n&#41;&#93; do
begin
tb&#58;= tb + w * b;
tg&#58;= tg + w * g;
tr&#58;= tr + w * r;
end;
end;
with P&#91;j&#93; do
begin
b&#58;= TrimReal&#40;0, 255, tb&#41;;
g&#58;= TrimReal&#40;0, 255, tg&#41;;
r&#58;= TrimReal&#40;0, 255, tr&#41;;
end;
end;

Move&#40;P&#91;0&#93;, theRow&#91;0&#93;, &#40;High&#40;theRow&#41; + 1&#41; * Sizeof&#40;TRGBTriple&#41;&#41;;
end;

procedure GBlur&#40;theBitmap&#58; TBitmap; radius&#58; single&#41;;
var Row, Col&#58; integer; theRows&#58; PPRows; K&#58; TKernel; ACol&#58; PRow; P&#58;PRow;
begin
if &#40;theBitmap.HandleType <> bmDIB&#41; or &#40;theBitmap.PixelFormat <> pf24Bit&#41; then
raise exception.Create&#40;'GBlur only works for 24-bit bitmaps'&#41;;


MakeGaussianKernel&#40;K, radius, 255, 1&#41;;
GetMem&#40;theRows, theBitmap.Height * SizeOf&#40;PRow&#41;&#41;;
GetMem&#40;ACol, theBitmap.Height * SizeOf&#40;TRGBTriple&#41;&#41;;

//record the location of the bitmap data&#58;
for Row&#58;= 0 to theBitmap.Height - 1 do
theRows&#91;Row&#93;&#58;= theBitmap.Scanline&#91;Row&#93;;

//blur each row&#58;
P&#58;= AllocMem&#40;theBitmap.Width*SizeOf&#40;TRGBTriple&#41;&#41;;
for Row&#58;= 0 to theBitmap.Height - 1 do
BlurRow&#40;Slice&#40;theRows&#91;Row&#93;^, theBitmap.Width&#41;, K, P&#41;;

//now blur each column
ReAllocMem&#40;P, theBitmap.Height*SizeOf&#40;TRGBTriple&#41;&#41;;
for Col&#58;= 0 to theBitmap.Width - 1 do
begin
//- first read the column into a TRow&#58;
for Row&#58;= 0 to theBitmap.Height - 1 do
ACol&#91;Row&#93;&#58;= theRows&#91;Row&#93;&#91;Col&#93;;


BlurRow&#40;Slice&#40;ACol^, theBitmap.Height&#41;, K, P&#41;;

//now put that row, um, column back into the data&#58;
for Row&#58;= 0 to theBitmap.Height - 1 do
theRows&#91;Row&#93;&#91;Col&#93;&#58;= ACol&#91;Row&#93;;
end;

FreeMem&#40;theRows&#41;;
FreeMem&#40;ACol&#41;;
ReAllocMem&#40;P, 0&#41;;
end;

end.

arthurprs
09-07-2007, 04:00 AM
&#123;



Ohh thx =)

anything about glow ?

JernejL
09-07-2007, 07:38 AM
glow is a variant of blur.. but i have nothing to work with that.

jasonf
09-07-2007, 09:57 AM
If you look in the Jedi-SDL library, there's a Kernel effect system which I ported from some C code a long time ago, it's still there though.

I think that what you need for Glow is Gradual Outlines at progressively darker colours.

Basically, you create a surface to contain your glow,
fill it with black
and you draw an outline of your sprite on it. So your new surface contains a nice outline of your sprite.

Then using a slightly darker colour, on the same surface, you draw an outline around your previous outline..

(You'll have to check for the colour of the previous outline or you'll end up bleeding colour where you don't want it)

Continue doing this until you're using a dark enough colour.

then, Additive Blit your new glow to the screen.

The technique is quite processor intensive, so you'll probably want to cache your results.

arthurprs
09-07-2007, 05:01 PM
If you look in the Jedi-SDL library, there's a Kernel effect system which I ported from some C code a long time ago, it's still there though.

I think that what you need for Glow is Gradual Outlines at progressively darker colours.

Basically, you create a surface to contain your glow,
fill it with black
and you draw an outline of your sprite on it. So your new surface contains a nice outline of your sprite.

Then using a slightly darker colour, on the same surface, you draw an outline around your previous outline..

(You'll have to check for the colour of the previous outline or you'll end up bleeding colour where you don't want it)

Continue doing this until you're using a dark enough colour.

then, Additive Blit your new glow to the screen.

The technique is quite processor intensive, so you'll probably want to cache your results.

Any ready code ?

jasonf
10-07-2007, 02:33 PM
Ready code?... Nope. But the code is very simple.


procedure outline&#40; destinationpixels, sourcepixels, transparent, outlinecolour&#41;

For x = 0 to sourcewidth

For y = 0 to sourceheight

if sourcepixels&#40; x,y &#41; = transparent and
&#40; sourcepixels&#40; x-1, y &#41; <> transparent or
sourcepixels&#40; x+1, y &#41; <> transparent or
sourcepixels&#40; x, y-1 &#41; <> transparent or
sourcepixels&#40; x, y+1 &#41; <> transparent &#41; then
destinationpixels&#40; x,y &#41; = outlinecolour
end if

next y

next x

end procedure



procedure makeglow&#40; destinationpixels, sourcepixels, transparentcolour, outlinecolour , outlinedarknessfactor, outlinesteps &#41;

outline&#40; destinationpixels, sourcepixels, transparent,outlinecolour &#41;


for i = 1 to outlinesteps

tempsource = copydestinationtotempdestination&#40; destinationpixels &#41;

outlinecolour = outlinecolour - outlinedarknessfactor

outline&#40; destinationpixels, tempsource, transparent, outlinecolour &#41;

next i

end procedure


sorry, I can't write it in pascal at the moment, I don't have access to a pascal IDE.. so I've done it in Pseudocode.

arthurprs
10-07-2007, 04:03 PM
Ready code?... Nope. But the code is very simple.


procedure outline&#40; destinationpixels, sourcepixels, transparent, outlinecolour&#41;

For x = 0 to sourcewidth

For y = 0 to sourceheight

if sourcepixels&#40; x,y &#41; = transparent and
&#40; sourcepixels&#40; x-1, y &#41; <> transparent or
sourcepixels&#40; x+1, y &#41; <> transparent or
sourcepixels&#40; x, y-1 &#41; <> transparent or
sourcepixels&#40; x, y+1 &#41; <> transparent &#41; then
destinationpixels&#40; x,y &#41; = outlinecolour
end if

next y

next x

end procedure



procedure makeglow&#40; destinationpixels, sourcepixels, transparentcolour, outlinecolour , outlinedarknessfactor, outlinesteps &#41;

outline&#40; destinationpixels, sourcepixels, transparent,outlinecolour &#41;


for i = 1 to outlinesteps

tempsource = copydestinationtotempdestination&#40; destinationpixels &#41;

outlinecolour = outlinecolour - outlinedarknessfactor

outline&#40; destinationpixels, tempsource, transparent, outlinecolour &#41;

next i

end procedure


sorry, I can't write it in pascal at the moment, I don't have access to a pascal IDE.. so I've done it in Pseudocode.

It looks like a MIX of Basic and Pascal

jasonf
11-07-2007, 08:13 AM
well, yeah, it's a kind-of mix of both.. needless to say, it won't compile.

Can you see what I'm trying to show you though?


You see, writing a glow function is pretty much library dependent so I can't just write the code. You'll need access to the surface memory so you can manipulate it. Different libraries expose the underlying surface in different ways, some give an array of pixels you can use.

If you're using the Jedi-SDL library then you're going to have a different mechanism to access the surface memory than with DelphiX.

Once you've written a function which works for your library, you'll only want to call it whenever your main image (the one which you're generating the glow for) changes.. otherwise, you'll eat processor cycles re-generating the same glow for every frame.

arthurprs
11-07-2007, 10:20 PM
well, yeah, it's a kind-of mix of both.. needless to say, it won't compile.

Can you see what I'm trying to show you though?


You see, writing a glow function is pretty much library dependent so I can't just write the code. You'll need access to the surface memory so you can manipulate it. Different libraries expose the underlying surface in different ways, some give an array of pixels you can use.

If you're using the Jedi-SDL library then you're going to have a different mechanism to access the surface memory than with DelphiX.

Once you've written a function which works for your library, you'll only want to call it whenever your main image (the one which you're generating the glow for) changes.. otherwise, you'll eat processor cycles re-generating the same glow for every frame.

I will try the code =)

I will post results later.

arthurprs
14-07-2007, 03:01 PM
function Tform1.outline( src : TBitmap ; transparentcolor , outlinecolor : TColor ): Tbitmap;
var
x, y: Integer;
begin

result:= TBitmap.Create;
zprofiler.Mark(1,True);
Result.Assign(src);
zprofiler.Mark(1,False);

for x := 1 to src.Width - 2 do
for y := 1 to src.Height - 2 do
begin
if (src.Canvas.Pixels[ x,y ] = transparentcolor) and
( (src.Canvas.Pixels[ x-1, y ] <> transparentcolor) or
(src.Canvas.Pixels[ x+1, y ] <> transparentcolor) or
(src.Canvas.Pixels[ x, y-1 ] <> transparentcolor) or
(src.Canvas.Pixels[ x, y+1 ] <> transparentcolor) ) then
Result.Canvas.Pixels[ x,y ] := outlinecolor;
end;
end;

function Tform1.makeglow(src : Tbitmap ; transparentcolor , glowcolor : TColor; outlinedarknessfactor, outlinesteps: Integer): TBitmap;
var
count : Integer;
begin
Result := TBitmap.Create;
Result.Assign(src);
for count:= 0 to outlinesteps-1 do
begin
Result := outline( Result, transparentcolor, glowcolor);
glowcolor := glowcolor + outlinedarknessfactor; // << this is wrong i know
end;
end;

procedure TForm1.Image1Click(Sender: TObject);
begin
image2.Picture.Bitmap:= makeglow(Image1.Picture.Bitmap, RGB(255,0,255), RGB(0,100,250), 100, 3);
end;


;] it appers to have worked

http://upload9.postimage.org/277064/Untitled_2.jpg (http://upload9.postimage.org/277064/photo_hosting.html)

But the code is toooooo slow :(
i think im making something wrong

EDIT: Reuploaded the img cuz jpeg is a TRASH to show details

JernejL
14-07-2007, 06:29 PM
Ofcourse it is slow on cpu! that's why people do such effects with graphic cards!

arthurprs
14-07-2007, 08:04 PM
Ofcourse it is slow on cpu! that's why people do such effects with graphic cards!

How can i make gpu do the work ?

JernejL
14-07-2007, 08:23 PM
by using opengl or d3d to do the fancy rendering.

wagenheimer
16-07-2007, 01:35 AM
There are some example of how to do this on Asphyre without using shaders?

Robert Kosek
16-07-2007, 02:12 AM
The general idea given, in the example of blurred vector/line games, was that you render to a small rendertarget, render normally, and then stretch the rendertarget over the screen with anti-aliasing on. Of course you must give it a transparency value and correct blendmode. It gives a pretty good result from what I understand.

Without shaders that is pretty much the best you can do. At least efficiently.

Andreaz
16-07-2007, 05:07 AM
Use Bitmap.Scanline instead of Bitmap.Canvas.Pixels[ x,y ] and you'll jump alot in speed;

The following code is to convert a Phoenix texture to a windows Bitmap showing the use of scanline:



Type TRGBQuadArray = Array&#91;WORD&#93; of TRGBQuad;
Type PRGBQuadArray = ^TRGBQuadArray;
Type TRGBTripleArray = Array&#91;WORD&#93; of TRGBTriple;
Type PRGBTripleArray = ^TRGBTripleArray;

var Line &#58; PRGBTripleArray;
var X,Y &#58; Integer;
var Color &#58; TColor4b;
begin
Dest.Width &#58;= Source.Width;
Dest.Height &#58;= Source.Height;
Dest.PixelFormat&#58;= Graphics.pf24Bit;

For Y&#58;=0 to Source.Height-1 do begin
Line&#58;=Dest.ScanLine&#91;Y&#93;;
For X&#58;=0 to Source.Width-1 do begin
Color&#58;= Source.Pixels&#91;X,Y&#93;;

Line&#91;X&#93;.rgbtRed &#58;= Color.Red ;
Line&#91;X&#93;.rgbtGreen &#58;= Color.Green;
Line&#91;X&#93;.rgbtBlue &#58;= Color.Blue ;
end;
end;

arthurprs
16-07-2007, 06:43 AM
Use Bitmap.Scanline instead of Bitmap.Canvas.Pixels[ x,y ] and you'll jump alot in speed;

The following code is to convert a Phoenix texture to a windows Bitmap showing the use of scanline:



Type TRGBQuadArray = Array&#91;WORD&#93; of TRGBQuad;
Type PRGBQuadArray = ^TRGBQuadArray;
Type TRGBTripleArray = Array&#91;WORD&#93; of TRGBTriple;
Type PRGBTripleArray = ^TRGBTripleArray;

var Line &#58; PRGBTripleArray;
var X,Y &#58; Integer;
var Color &#58; TColor4b;
begin
Dest.Width &#58;= Source.Width;
Dest.Height &#58;= Source.Height;
Dest.PixelFormat&#58;= Graphics.pf24Bit;

For Y&#58;=0 to Source.Height-1 do begin
Line&#58;=Dest.ScanLine&#91;Y&#93;;
For X&#58;=0 to Source.Width-1 do begin
Color&#58;= Source.Pixels&#91;X,Y&#93;;

Line&#91;X&#93;.rgbtRed &#58;= Color.Red ;
Line&#91;X&#93;.rgbtGreen &#58;= Color.Green;
Line&#91;X&#93;.rgbtBlue &#58;= Color.Blue ;
end;
end;



I will try =]

jasonf
16-07-2007, 10:05 AM
Also, Cache your results wherever possible, don't regenerate glows for images you've already generated glows for.

To speed the process up, if you have a small number of sprites, you could generate glows for all of your sprites before the game begins.. or generate and cache them as you go if you have more.

By doing this, you, and not making your glows for every render, you'll improve your performance enormously.

How you implement your cache system is up to you though, I could suggest something, but it might be incompatible with your game engine.

arthurprs
16-07-2007, 10:23 PM
Also, Cache your results wherever possible, don't regenerate glows for images you've already generated glows for.

To speed the process up, if you have a small number of sprites, you could generate glows for all of your sprites before the game begins.. or generate and cache them as you go if you have more.

By doing this, you, and not making your glows for every render, you'll improve your performance enormously.

How you implement your cache system is up to you though, I could suggest something, but it might be incompatible with your game engine.

Suggest :P

jasonf
17-07-2007, 07:56 PM
Well, one way would be to use a list of some type, something which gives you access to objects contained within using a key. I use a TStringList

Then, all you need to do is.

1. Drawing routine wants to draw a glow for sprite named "MAN"

2. Check GlowCache (aformentioned list), If glowsurface exists, return it.

2.1 If glow surface does not exist, render the glow to a new surface for the sprite named "MAN" and add the glow to the cache with the key of "MAN", return it.

3. Render the glow to the destination surface.


From this point on, all calls for a glow for the sprite "MAN" will return a pre-generated glow.