Конференция "Media" » Как в GDI+ отрисовать TBitmap с полупрозрачностью? [D6, Win2k, WinXP]
 
  • Валигози © (22.09.09 09:29) [0]
    Допустим есть у меня переменная bmp типа TBitmap, которую я загружаю из файла или с ресурсов (вобщем неважно).
    Теперь на его основе я создаю объект GDI+:
    gpaBitmap:=nil;
    GdipCreateBitmapFromHBITMAP(bmp.Handle, bmp.Palette, gpaBitmap);

    И как бы теперь нарисовать этот Bitmap не просто: GdipDrawImage(gpaGraphics, gpaBitmap, 0, 0), а с полупрозрачностью (с использованием альфа канала)?
    Может этот gpaBitmap нужно предварительно какой то функцией обработать?

    P.S.
    uses ..., GDIPAPI;
    var
     bmp: TBitmap;
     gpaGraphics: GpGraphics;
     gpaBitmap: GpBitmap;
    begin
     GdipCreateFromHDC(Form1.Canvas.Handle, gpaGraphics);
     ...
  • Валигози © (24.09.09 18:08) [1]
    Странно, что на мой вопрос никто не дал даже самый глупый ответ (что бывает в этом форуме чаще чем хотелось бы) :)
    Для тех кому интересно - мне таки удалось добиться нужного результата. Это оказалось вовсе не интуитиво...
    Что бы нарисовать Bitmap с нужной степенью прозрачности необходимо:
    1) создать объект GpImageAttributes
    2) заполнить странным способом массив TColorMatrix
    3) вызвать функцию GdipSetImageAttributesColorMatrix указав ей объект GpImageAttributes и массив TColorMatrix
    4) и наконец вызвать страшную функцию GdipDrawImageRectRectI требующую кучу аргументов, большинство которых для данной задачи либо дублируется либо и вовсе пустые.
    Вот собственно функия-оболочка для указанных действий:
    procedure DrawAlphaBitmap2(gpaGraphics: GpGraphics; Bitmap: TBitmap; X, Y: Integer; BitmapAlpha: Byte);
    var
     gpaBitmap: GpBitmap;
     gpaImgAttr: GpImageAttributes;
     gpaColorMatrix: TColorMatrix;
    begin
     gpaBitmap:=nil;
     GdipCreateBitmapFromHBITMAP(Bitmap.Handle, Bitmap.Palette, gpaBitmap);
     try
       gpaImgAttr:=nil;
       GdipCreateImageAttributes(gpaImgAttr);
       try
         FillChar(gpaColorMatrix, SizeOf(gpaColorMatrix), 0);
         gpaColorMatrix[0, 0]:=1;
         gpaColorMatrix[1, 1]:=1;
         gpaColorMatrix[2, 2]:=1;
         gpaColorMatrix[3, 3]:=BitmapAlpha/255;
         gpaColorMatrix[4, 4]:=1;

         GdipSetImageAttributesColorMatrix(
           gpaImgAttr,
           ColorAdjustTypeDefault,
           True,
           @gpaColorMatrix,
           nil,
           ColorMatrixFlagsDefault);

         GdipDrawImageRectRectI(
           gpaGraphics,
           gpaBitmap,
           X,
           Y,
           Bitmap.Width,
           Bitmap.Height,
           0,
           0,
           Bitmap.Width,
           Bitmap.Height,
           UnitPixel,
           gpaImgAttr,
           nil,
           nil);
       finally
         GdipDisposeImageAttributes(gpaImgAttr);
       end;
     
     finally
       GdipDisposeImage(gpaBitmap);
     end;
    end;



    Эта функция принимает параметры:
    gpaGraphics - объект Graphics на котором следует рисовать
    Bitmap - Bitmap который и нужно нарисовать
    X, Y - координаты верхнего левого угла
    BitmapAlpha - степень прозрачности от 0 до 255 - собственно то, ради чего всё и затевалось... :)

    Пример использования:
    procedure TForm1.Button1Click(Sender: TObject);
    var
     bmp: TBitmap;
     gpaGraphics: GpGraphics;
    begin
     Canvas.Brush.Color:=clWhite;
     Canvas.FillRect(Rect(0, 0, 400, 400));

     bmp:=TBitmap.Create;
     try
       GdipCreateFromHDC(Canvas.Handle, gpaGraphics);
       try
         bmp.LoadFromFile('C:\WINDOWS\Пузыри.bmp');
         DrawAlphaBitmap2(gpaGraphics, bmp, 30, 40, 50);
         bmp.LoadFromFile('C:\WINDOWS\Паркет.bmp');
         DrawAlphaBitmap2(gpaGraphics, bmp, 10, 10, 150);
       finally
         GdipDeleteGraphics(gpaGraphics);
       end;

     finally
       bmp.Free;
     end;
    end;



    Ещё в uses нужно добавить GDIPAPI (этот модуль можно скачать в интернете) и ещё неплохо добавить GDIPOBJ, ну или же вручную проинициализировать GDI+

    И ещё приведу процедуру, которую я наваял ещё до того как написал в форум этот вопрос и от которой я пытался избавиться, так как она работает через ScanLine, да и вобще кривоватая.
    Она вполне рабочая, но как впоследствии оказалось всё же медленнее, чем мой второй вариант который я привёл выше. Скорее всего это изза того, что GpBitmap приходится создавать дважды: сначала функцией GdipCreateBitmapFromHBITMAP по образу TBitmap, а потом функцией GdipCloneBitmapArea, чтобы добавить к GpBitmap альфа-канал. Да и принцип получения ScanLine другой: в стандартном GDI просто даётся указатель на область памяти где хранится сам Bitmap, а в GDI+ создаётся временный буфер (функцией GdipBitmapLockBits), в него копируется (если указан флаг ImageLockModeRead) заданная область в заданном пиксельном формате, а по завершению (функцией GdipBitmapUnlockBits) содержимое этого буфера вновь копируется (если указан флаг ImageLockModeWrite) в GpBitmap...

    procedure DrawAlphaBitmap0(gpaGraphics: GpGraphics; Bitmap: TBitmap; X, Y: Integer; BitmapAlpha: Byte);
    type
     PGPColorRec = ^TGPColorRec;
     TGPColorRec = packed record
       b, g, r, a: Byte;
     end;
    var
     gpaBitmap, gpaBitmap2: GpBitmap;
     gpaRect: TGPRect;
     gpaPColor: PGPColorRec;
     gpaBitmapData: TBitmapData;
     xx, yy: Integer;  
    begin
     gpaBitmap:=nil;
     GdipCreateBitmapFromHBITMAP(Bitmap.Handle, Bitmap.Palette, gpaBitmap);
     try
       GdipCloneBitmapArea(0, 0, Bitmap.Width, Bitmap.Height, PixelFormat32bppARGB, gpaBitmap, gpaBitmap2);
       try
         gpaRect:=MakeRect(0, 0, Bitmap.Width, Bitmap.Height);
         GdipBitmapLockBits(
           gpaBitmap2,
           @gpaRect,
           ImageLockModeRead or ImageLockModeWrite,
           PixelFormat32bppARGB,
           @gpaBitmapData);
         try
           gpaPColor:=gpaBitmapData.Scan0;

           for yy := 0 to gpaBitmapData.Height-1 do
           begin
             gpaPColor:=Pointer(Integer(gpaBitmapData.Scan0)+yy*gpaBitmapData.Stride);

             for xx := 1 to gpaBitmapData.Width do
             begin
               gpaPColor.a:=BitmapAlpha;
               Inc(gpaPColor);
             end;
           end;

         finally
           GdipBitmapUnlockBits(gpaBitmap2, @gpaBitmapData);
         end;

         GdipDrawImage(gpaGraphics, gpaBitmap2, X, Y);
       finally
         GdipDisposeImage(gpaBitmap2);
       end;
     
     finally
       GdipDisposeImage(gpaBitmap);
     end;
    end;

  • antonn © (24.09.09 18:20) [2]

    > Валигози ©   (24.09.09 18:08) [1]
    >
    > Странно, что на мой вопрос никто не дал даже самый глупый
    > ответ (что бывает в этом форуме чаще чем хотелось бы) :)

    наверное люди привыкли решать поставленную задачу на "обычном" GDI =)
  • Валигози © (25.09.09 11:35) [3]

    > antonn ©   (24.09.09 18:20) [2]
    >
    > наверное люди привыкли решать поставленную задачу на "обычном"
    > GDI =)

    А как такое можно сделать на обычном GDI?
  • MBo © (25.09.09 11:40) [4]
    AlphaBlend function displays bitmaps that have transparent or semitransparent pixels
  • antonn © (25.09.09 15:59) [5]
    можно Альфаблендом, можно ручками создавать временный битмап, на него копировать канву, блитить картинку и копировать канву обратно :)
  • antonn © (25.09.09 16:43) [6]
    примерно так:
    procedure BlendBitmap_GDI(ACanvas: TCanvas; BT:Tbitmap; x,y:integer);
    const
     MaxPixelCountA = MaxInt div SizeOf(TRGBQuad);
    type
     PRGBAArray = ^TRGBAArray;
     TRGBAArray = array[0..MaxPixelCountA-1] of TRGBQuad;
    var xTo,sx,YTo,ddx,ddy,sy,w,h,dstw,dsth: integer;
       bt_buff:Tbitmap; i,ii,itemp:integer; RSource,RDest:PRGBAArray; dd:double;
    begin
     w:=BT.Width;
     h:=bt.Height;
     dstw:=ACanvas.ClipRect.Right-ACanvas.ClipRect.Left;
     dsth:=ACanvas.ClipRect.Bottom-ACanvas.ClipRect.Top;
     XTo:=x+W-1; YTo:=y+H-1;
     if (y>=dstH) or (x>=dstW) or (YTo<0) or (XTo<0) then exit;
     ddx:=0; ddy:=0;
     sx:=W; sy:=H;
     if X<0 then begin
         ddx:=-X;
         inc(sx,X);
         x:=0;
     end;
     if Y<0 then begin
         ddy:=-Y;
         inc(sy,Y);
         y:=0;
     end;
     if XTo>=dstw then dec(sx,XTo-dstw+1);
     if YTo>=dsth then dec(sy,YTo-dsth+1);
     if (sx<=0) or (sy<=0) then exit;
     if(BT.PixelFormat<>pf32bit) then exit;

     bt_buff:=Tbitmap.Create;
     try
       bt_buff.Width:=sx;
       bt_buff.Height:=sy;
       bt_buff.canvas.CopyRect( rect(0,0,bt_buff.Width,bt_buff.Height), ACanvas ,rect(x,y,x+sx,y+sy) );
       bt_buff.PixelFormat:=pf32bit;
       for i:=0 to sy-1 do begin
         RSource:=BT.ScanLine[i+ddy];
         RDest:=bt_buff.ScanLine[i];
         for ii:=0 to sx-1 do begin
           dd:=((100/255)/100)*RSource[ii+ddx].rgbReserved;

           itemp:=round(RDest[ii].rgbRed+(RSource[ii+ddx].rgbRed-RDest[ii].rgbRed)*dd);
           if itemp>255 then itemp:=255 else if itemp<0 then itemp:=0;
           RDest[ii].rgbRed:=itemp;

           itemp:=round(RDest[ii].rgbGreen+(RSource[ii+ddx].rgbGreen-RDest[ii].rgbGreen)*dd);
           if itemp>255 then itemp:=255 else if itemp<0 then itemp:=0;
           RDest[ii].rgbGreen:=itemp;

           itemp:=round(RDest[ii].rgbBlue+(RSource[ii+ddx].rgbBlue-RDest[ii].rgbBlue)*dd);
           if itemp>255 then itemp:=255 else if itemp<0 then itemp:=0;
           RDest[ii].rgbBlue:=itemp;
         end;
       end;
       ACanvas.CopyRect( rect(x,y,x+sx,y+sy), bt_buff.canvas ,rect(0,0,bt_buff.Width,bt_buff.Height) );
     finally
       bt_buff.Free;
     end;
    end;

  • Don (30.10.09 21:43) [7]

    > Странно, что на мой вопрос никто не дал даже самый глупый
    > ответ (что бывает в этом форуме чаще чем хотелось бы) :)


    Абсолютно с вами согласен.
 
Конференция "Media" » Как в GDI+ отрисовать TBitmap с полупрозрачностью? [D6, Win2k, WinXP]
Есть новые Нет новых   [134430   +1][b:0][p:0.004]