Board index » delphi » ColorToRGB for use in shading

ColorToRGB for use in shading

I tried this approach to shade my bitmap, but it sets every pixel
black, and I don't understand why...

procedure SetShaded(const bShaded: boolean);
var
  x,y:integer;
  bmp:TBitMap;
begin
  bmp:=TImage1.Picture.Bitmap;

  for x:=0 to bmp.Width -1 do
    for y:= 0 to bmp.Height -1 do
      if bShaded then
         bmp.Canvas.pixels[x,y]:=ColorToRGB(bmp.canvas.pixels[x,y])-1
      else
         bmp.canvas.pixels[x,y]:=ColorToRGB(bmp.canvas.pixels[x,y])+1
end;

Any ideas how to accomplish this? Thanks!

 

Re:ColorToRGB for use in shading


use this :

tempcolor:=bmp.canvas.pixels[x,y];
gray:= (getrvalue(tempcolor)+getgvalue(tempcolor)+getbvalue(tempcolor))div
3;
bmp.canvas.pixels[x,y]:=rgbtocolor(gray,gray,gray);

this method is slow however , you may want to access bitmap pixels by using
scanline property that is many times faster than canvas.pixels property.

"Denny Gregory" <dgreg...@csonline.com> ha scritto nel messaggio
news:3cfd1918_1@dnews...

Quote

> I tried this approach to shade my bitmap, but it sets every pixel
> black, and I don't understand why...

> procedure SetShaded(const bShaded: boolean);
> var
>   x,y:integer;
>   bmp:TBitMap;
> begin
>   bmp:=TImage1.Picture.Bitmap;

>   for x:=0 to bmp.Width -1 do
>     for y:= 0 to bmp.Height -1 do
>       if bShaded then
>          bmp.Canvas.pixels[x,y]:=ColorToRGB(bmp.canvas.pixels[x,y])-1
>       else
>          bmp.canvas.pixels[x,y]:=ColorToRGB(bmp.canvas.pixels[x,y])+1
> end;

> Any ideas how to accomplish this? Thanks!

Re:ColorToRGB for use in shading


sorry i gave you the code for grayscale not for shading as you required :

 to shade you can use this instead :

tempcolor:=bmp.canvas.pixels[x,y];
bmp.canvas.pixels[x,y]:=rgbtocolor(getrvalue(tempcolor)-20,getgvalue(tempcol
or)-20,getbvalue(tempcolor)-20);

"Francesco Savastano" <francos...@libero.it> ha scritto nel messaggio
news:3cfd36ab_1@dnews...

Quote
> use this :

> tempcolor:=bmp.canvas.pixels[x,y];
> gray:= (getrvalue(tempcolor)+getgvalue(tempcolor)+getbvalue(tempcolor))div
> 3;
> bmp.canvas.pixels[x,y]:=rgbtocolor(gray,gray,gray);

> this method is slow however , you may want to access bitmap pixels by
using
> scanline property that is many times faster than canvas.pixels property.

> "Denny Gregory" <dgreg...@csonline.com> ha scritto nel messaggio
> news:3cfd1918_1@dnews...

> > I tried this approach to shade my bitmap, but it sets every pixel
> > black, and I don't understand why...

> > procedure SetShaded(const bShaded: boolean);
> > var
> >   x,y:integer;
> >   bmp:TBitMap;
> > begin
> >   bmp:=TImage1.Picture.Bitmap;

> >   for x:=0 to bmp.Width -1 do
> >     for y:= 0 to bmp.Height -1 do
> >       if bShaded then
> >          bmp.Canvas.pixels[x,y]:=ColorToRGB(bmp.canvas.pixels[x,y])-1
> >       else
> >          bmp.canvas.pixels[x,y]:=ColorToRGB(bmp.canvas.pixels[x,y])+1
> > end;

> > Any ideas how to accomplish this? Thanks!

Re:ColorToRGB for use in shading


Quote
"Francesco Savastano" <francos...@libero.it> wrote:
>sorry i gave you the code for grayscale not for shading as you required :

> to shade you can use this instead :

>tempcolor:=bmp.canvas.pixels[x,y];
>bmp.canvas.pixels[x,y]:=rgbtocolor(getrvalue(tempcolor)-20,getgvalue(tempcol
>or)-20,getbvalue(tempcolor)-20);

Thank you very much! Can this be done using the Scanline
method?

Re:ColorToRGB for use in shading


Quote
> Thank you very much! Can this be done using the Scanline
> method?

if you know the Pixelformat of the bitmap, this can be done the following
way (for pf24bit)...

type
  TPix24 = packed array[0..2] of byte;
  PPix24 = ^TPix24;
[...]

var
  Ptr24: PPix24;
  R, G, B: Integer;
[...]

for y := 0 to Bitmap.Height - 1 do begin
  Ptr24 := Bitmap.Scanline[y];
  for x := 0 to Bitmap.Width - 1 do begin
    R := Ptr24^[0];
    G := Ptr24^[1];
    B := Ptr24^[2];
    R := R-20;
    G := G-20;
    B := B-20;
    if R < 0 then R := 0;
    [...]
    Ptr24^[0] := R;
    [...]
    Inc (Ptr24);
  end;
end;

Jens

Re:ColorToRGB for use in shading


  [...]
  bmp:=Self.Picture.Bitmap;
  if bShaded then val:=-20 else val:=20;
  for y := 0 to bmp.Height - 1 do begin
    Ptr24 := bmp.Scanline[y];
    for x := 0 to bmp.Width - 1 do begin
      R := Ptr24^[0];
      G := Ptr24^[1];
      B := Ptr24^[2];
      R := R+val;
      G := G+val;
      B := B+val;
      if R < 0 then R := 0;
      if G < 0 then G := 0;
      if B < 0 then B := 0;
      Ptr24^[0] := R;
      Ptr24^[1] := G;
      Ptr24^[2] := B;
      Inc (Ptr24);
    end;
  end;

I don't know what Pixelformat it is, so I put in a break point
and it tells me "pfDevice"

Quote
"Jens Gruschel" <grusc...@esteam.de> wrote:
>> Wow, that totally crashes my computer every time...

>Are you sure your bitmap has Pixelformat pf24bit? What is your code at the
>places marked with [...]?

>Jens

Re:ColorToRGB for use in shading


Quote
> Wow, that totally crashes my computer every time...

Are you sure your bitmap has Pixelformat pf24bit? What is your code at the
places marked with [...]?

Jens

Re:ColorToRGB for use in shading


Wow, that totally crashes my computer every time...

Quote
"Jens Gruschel" <grusc...@esteam.de> wrote:
>> Thank you very much! Can this be done using the Scanline
>> method?

>if you know the Pixelformat of the bitmap, this can be done the following
>way (for pf24bit)...

>type
>  TPix24 = packed array[0..2] of byte;
>  PPix24 = ^TPix24;
>[...]

>var
>  Ptr24: PPix24;
>  R, G, B: Integer;
>[...]

>for y := 0 to Bitmap.Height - 1 do begin
>  Ptr24 := Bitmap.Scanline[y];
>  for x := 0 to Bitmap.Width - 1 do begin
>    R := Ptr24^[0];
>    G := Ptr24^[1];
>    B := Ptr24^[2];
>    R := R-20;
>    G := G-20;
>    B := B-20;
>    if R < 0 then R := 0;
>    [...]
>    Ptr24^[0] := R;
>    [...]
>    Inc (Ptr24);
>  end;
>end;

>Jens

Re:ColorToRGB for use in shading


Quote
> I don't know what Pixelformat it is, so I put in a break point
> and it tells me "pfDevice"

If you access bitmaps with pointers (ScanLine returns a pointer to the pixel
data), you have to make sure the pointers really point to the bitmap, not
somewhere else in your memory. The pointer ScanLine returns is valid, but if
you move it along the line (Inc (Ptr24)), it can become invalid. So you may
not increase it by more bytes than your line has bytes. The length of a line
(in bytes) depends on the PixelFormat of your bitmap. For example for
PixelFormat pf24bit each pixel takes 3 bytes. If your pointer is of a type
that takes 3 bytes (packed array[0..2] or a packed record that contains 3
bytes) it is moved by 3 bytes each time you increase or decrease it by one.
If you use such a pointer for a bitmap with other pixelformat (or increase
the pointer too many times), the pointer does not neccessarily point to a
valid pixel any more. So you need a separate routine for each pixelformat
(pfDevice is a bit difficult to handle because it means the format of your
current screen settings) or you have to make sure the bitmap has the right
format (for example by setting Bitmap.PixelFormat := pf24bit, doing this
before you change its size is better than afterwards). pf32bit is very
similar to pf24bit (it has one empty byte for each pixel that sometimes is
used for alpha channel), needs more memory but can be accessed faster. For
pf8bit you have to change the palette instead of the pixel data, other
formats are a bit difficult to handle (you need some bit shifting for 16
bit). Hope that helps...

Jens

Re:ColorToRGB for use in shading


Thanks for your time and patience on this issue. From what I now
understand about Scanline, I think I want to avoid it. My new
approach is to use an overlay to have a shaded affect. I found
this code in the Google archives:

procedure DrawShaded(DestCanvas: TCanvas; X,Y: smallint;
SrcBitmap: TBitmap; AColor: TColor);
var
  ANDBitmap, ORBitmap: TBitmap;
  CM: TCopyMode;
  Src: TRect;
begin
  ANDBitmap:= NIL;
  ORBitmap:= NIL;
  try
    ANDBitmap:= TBitmap.Create;
    ORBitmap:= TBitmap.Create;
    Src := Bounds(0,0, SrcBitmap.Width, SrcBitmap.Height);

    ORBitmap.Width:= SrcBitmap.Width;
    ORBitmap.Height:= SrcBitmap.Height;
    ORBitmap.Canvas.Brush.Color := clBlack;
    ORBitmap.Canvas.CopyMode := cmSrcCopy;
    ORBitmap.Canvas.BrushCopy(Src, SrcBitmap, Src, AColor);

    ANDBitmap.Width:= SrcBitmap.Width;
    ANDBitmap.Height:= SrcBitmap.Height;
    ANDBitmap.Canvas.Brush.Color := clWhite;
    ANDBitmap.Canvas.CopyMode := cmSrcInvert;
    ANDBitmap.Canvas.BrushCopy(Src, SrcBitmap, Src, AColor);

    CM := DestCanvas.CopyMode;
    DestCanvas.CopyMode := cmSrcAnd;
    DestCanvas.Draw(X,Y, ANDBitmap);
    DestCanvas.CopyMode := cmSrcPaint;
    DestCanvas.Draw(X,Y, ORBitmap);
    DestCanvas.CopyMode := CM;

  finally
    ANDBitmap.Free;
    ORBitmap.Free;
  end;
end;

Now, I just have to figure out what this does and make it work
with my code. For example, I'm not sure what to send in as X,Y

Quote
"Jens Gruschel" <grusc...@esteam.de> wrote:
>> I don't know what Pixelformat it is, so I put in a break point
>> and it tells me "pfDevice"

>If you access bitmaps with pointers (ScanLine returns a pointer to the pixel
>data), you have to make sure the pointers really point to the bitmap, not
>somewhere else in your memory. The pointer ScanLine returns is valid, but if
>you move it along the line (Inc (Ptr24)), it can become invalid. So you may
>not increase it by more bytes than your line has bytes. The length of a line
>(in bytes) depends on the PixelFormat of your bitmap. For example for
>PixelFormat pf24bit each pixel takes 3 bytes. If your pointer is of a type
>that takes 3 bytes (packed array[0..2] or a packed record that contains 3
>bytes) it is moved by 3 bytes each time you increase or decrease it by one.
>If you use such a pointer for a bitmap with other pixelformat (or increase
>the pointer too many times), the pointer does not neccessarily point to a
>valid pixel any more. So you need a separate routine for each pixelformat
>(pfDevice is a bit difficult to handle because it means the format of your
>current screen settings) or you have to make sure the bitmap has the right
>format (for example by setting Bitmap.PixelFormat := pf24bit, doing this
>before you change its size is better than afterwards). pf32bit is very
>similar to pf24bit (it has one empty byte for each pixel that sometimes is
>used for alpha channel), needs more memory but can be accessed faster. For
>pf8bit you have to change the palette instead of the pixel data, other
>formats are a bit difficult to handle (you need some bit shifting for 16
>bit). Hope that helps...

>Jens

Re:ColorToRGB for use in shading


Denny,

Don't be afraid of ScanLine.
To make Jens code work for you,

simply do something like this

Bitmap := TBitmap.Create;
Bitmap.LoadFromFile(MyFile);
Bitmap.PixelFormat := pf24Bit;

Setting the pixel format will make sure the Bitmap is compatible with Jens
routine.

Regards

Stan.

Re:ColorToRGB for use in shading


Quote
> Thanks for your time and patience on this issue. From what I now
> understand about Scanline, I think I want to avoid it.

There are advantages and disadvantages. In my opinion for modern looking
GUIs (with gradients, alpha blending etc.) it is unavoidable accessing the
single RGB values directly (except if you use GDI+ which does all of this
stuff internally). On the other hand sometimes it's not worth for more
simple things.

Quote
> Now, I just have to figure out what this does and make it work
> with my code. For example, I'm not sure what to send in as X,Y

It's the coordinates of the bitmap you want to draw shaded within the
canvas. Note that probably the result won't look like the result of the
SetPixel / ScanLine routines.

Jens

Re:ColorToRGB for use in shading


So if I have a 2D array of data values that I want to display as a
bitmap image with color proportional to data value, do I need to
prepare the bimap differently for each pixelformat ?  I would need to
define different color values for either 16, 24 or 32 bit formats ?
Or could one routine be used (say 32 bit) to create a pf32bit bitmap,
and after the data has been mapped to it, just change the pixelformat
definition and the bitmap colors will automatically rescale to be
displayed with the proper proportion ?

On Wed, 5 Jun 2002 15:43:06 +0200, "Jens Gruschel"

Quote
<grusc...@esteam.de> wrote:
>If you use such a pointer for a bitmap with other pixelformat (or increase
>the pointer too many times), the pointer does not neccessarily point to a
>valid pixel any more. So you need a separate routine for each pixelformat
>(pfDevice is a bit difficult to handle because it means the format of your
>current screen settings) or you have to make sure the bitmap has the right
>format (for example by setting Bitmap.PixelFormat := pf24bit, doing this
>before you change its size is better than afterwards). pf32bit is very

Re:ColorToRGB for use in shading


Quote
> Or could one routine be used (say 32 bit) to create a pf32bit bitmap,
> and after the data has been mapped to it, just change the pixelformat
> definition and the bitmap colors will automatically rescale to be
> displayed with the proper proportion ?

> On Wed, 5 Jun 2002 15:43:06 +0200, "Jens Gruschel"
> <grusc...@esteam.de> wrote:

Yes  ! Windows will change the colors automatically when you change the
pixel format : however you must be careful with pixel format less than 16
bit (example 8 bit ) : they will need an optimized palette ( windows
standard palette gives often awful results )

Re:ColorToRGB for use in shading


Quote
> So if I have a 2D array of data values that I want to display as a
> bitmap image with color proportional to data value, do I need to
> prepare the bimap differently for each pixelformat ?  I would need to
> define different color values for either 16, 24 or 32 bit formats ?
> Or could one routine be used (say 32 bit) to create a pf32bit bitmap,
> and after the data has been mapped to it, just change the pixelformat
> definition and the bitmap colors will automatically rescale to be
> displayed with the proper proportion ?

Just set PixelFormat to pf32bit or whatever before you make your changes to
it. I've heard that some older drivers cannot handle all possible formats,
but I've never "met" such a driver (does someone know more?). But if you
want to be sure check the PixelFormat property after you've changed it, to
be sure it is really set to the value you wanted (and display an error
message otherwise, I guess this message will never be seen on 99.9% of all
machines).

If you want to change an existing bitmap with direct pointer access, but
save it with the same format than it had before, you can create a new
bitmap, set the new bitmap's pixelformat to pf32bit (or whatever), give it
the same size than the old bitmap had, draw the old bitmap on it, make your
changes, and then draw it back on the original bitmap.

Jens

Go to page: [1] [2]

Other Threads