[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]

    Program Transparent;
    {                                       }
    {   Example of How Transparency Works   }
    {                                       }
    {  Programmed by David Dahl @ 1:272/38  }
    {                                       }
    {     This program is PUBLIC DOMAIN     }
    {                                       }
    Uses CRT, Palette;

    Type ImageArray = Array [0..15, 0..15] of Byte;

         LocationRec = Record
                             X : Integer;
                             Y : Integer;
                       End;

         VGABufferArray = Array[0..199, 0..319] of Byte;
         VGABufferPtr   = ^VGABufferArray;

    Const BobTemplate : ImageArray =
                  ((00,00,00,00,00,00,07,07,07,07,00,00,00,00,00,00),
                   (00,00,00,00,07,07,04,04,04,04,06,05,00,00,00,00),
                   (00,00,00,07,04,04,04,04,04,04,04,04,04,00,00,00),
                   (00,00,07,04,04,04,04,04,04,04,04,04,04,03,00,00),
                   (00,07,04,04,04,04,04,04,04,04,04,04,04,04,02,00),
                   (00,07,04,04,04,04,04,04,04,04,04,04,04,04,01,00),
                   (07,04,04,04,04,04,04,04,04,04,04,04,04,04,04,01),
                   (07,04,04,04,04,04,04,04,04,04,04,04,04,04,04,01),
                   (07,04,04,04,04,04,04,04,04,04,04,04,04,04,04,01),
                   (07,04,04,04,04,04,04,04,04,04,04,04,04,04,04,01),
                   (00,06,04,04,04,04,04,04,04,04,04,04,04,04,01,00),
                   (00,06,04,04,04,04,04,04,04,04,04,04,04,04,01,00),
                   (00,00,05,04,04,04,04,04,04,04,04,04,04,01,00,00),
                   (00,00,00,04,04,04,04,04,04,04,04,04,01,00,00,00),
                   (00,00,00,00,03,02,04,04,04,04,01,01,00,00,00,00),
                   (00,00,00,00,00,00,01,01,01,01,00,00,00,00,00,00));

          MaxBob = 2; { 3 Bobs (0 .. 2) }

    Var VGA        : VGABufferPtr;
        BackGround : VGABufferPtr;
        WorkPage   : VGABufferPtr;

        Pal : PaletteArray;

        BobImage    : Array[0..MaxBob] of ImageArray;
        BobLocation : Array[0..MaxBob] of LocationRec;

        Counter1 : Integer;
        Counter2 : Integer;

    {-[ Set VGA Mode 13h (320 X 200 X 256 Chain 4) ]------------------------}
    Procedure SetMode13h; Assembler;
    ASM
       MOV AX, $13
       INT $10
    End;
    {-[ Put A 16 X 16 Image by ORing it With Background ]-------------------}
    Procedure Put16X16ImageOR (Var Bob    : ImageArray;
                                   X, Y   : Integer);
    Var CounterX,
        CounterY  : Integer;
    Begin
         For CounterY := 0 to 15 do
          For CounterX := 0 to 15 do
           WorkPage^[CounterY + Y, CounterX + X] :=
            WorkPage^[CounterY + Y, CounterX + X] OR Bob[CounterX, CounterY];
    End;
    {-[ Update Bob Positions ]----------------------------------------------}
    Procedure UpdateBobs;
    Var BobCounter : Integer;
    Begin
         For BobCounter := 0 to MaxBob do
         Begin
              Inc (Counter1, 1);
              While (Counter1 >= 360) do
                 Dec(Counter1, 360);

              If (Counter1 MOD 2) = 0
              Then
              Begin
                   Inc(Counter2,1);
                   While (Counter2 >= 360) do
                         Dec(Counter2, 360);
              End;

              BobLocation[BobCounter].X := 160 +
                 Round(90 * -Sin((Counter1 + (BobCounter*Counter2))*PI/180));

              BobLocation[BobCounter].Y := 95 +
                 Round(60 * Cos((Counter2 + (BobCounter*Counter1))*PI/180));

         End;
    End;
    {-[ Draw All Bobs To Work Buffer ]--------------------------------------}
    Procedure DrawBobs;
    Var BobCounter : Integer;
    Begin
         For BobCounter := 0 to MaxBob do
             Put16X16ImageOR (BobImage[BobCounter],
                BobLocation[BobCounter].X, BobLocation[BobCounter].Y);
    End;
    {-[ Initialize Variables ]----------------------------------------------}
    Procedure InitializeVariables;
    Const Tbl : Array [0..MaxBob] of Byte = (8, 16, 32);
    Var BobCounter : Integer;
        CX, CY     : Integer;
    Begin
         { Make Individual Bobs From Template }
         For BobCounter := 0 to MaxBob do
         Begin
              BobImage[BobCounter] := BobTemplate;

              For CY := 0 to 15 do
                  For CX := 0 to 15 do
                      If BobImage[BobCounter][CX,CY] <> 0
                      Then
                          BobImage[BobCounter][CX,CY] :=
                             BobImage[BobCounter][CX,CY] OR Tbl[BobCounter];
         End;

         Counter1 := 0;
         Counter2 := 0;
    End;
    {-[ Build Palette ]-----------------------------------------------------}
    Procedure BuildPalette;
    Var ColorCounter : Integer;
    Begin
         { Initialize Palette Buffer To All Black }
         FillChar (Pal, SizeOf(Pal), 0);

         For ColorCounter := 0 to 7 do
         Begin
          { Make Red, Green, and Blue Bobs }
          Pal[ColorCounter OR 08].Red   := 21 + (ColorCounter * 6);
          Pal[ColorCounter OR 16].Green := 21 + (ColorCounter * 6);
          Pal[ColorCounter OR 32].Blue  := 21 + (ColorCounter * 6);

          { Make Colors Where Red and Green Bobs Overlap }
          Pal[ColorCounter OR 08 OR 16].Red   := 21 + (ColorCounter * 6);
          Pal[ColorCounter OR 08 OR 16].Green := 21 + (ColorCounter * 6);

          { Make Colors Where Red and Blue Bobs Overlap }
          Pal[ColorCounter OR 08 OR 32].Red  := 21 + (ColorCounter * 6);
          Pal[ColorCounter OR 08 OR 32].Blue := 21 + (ColorCounter * 6);

          { Make Colors Where Green and Blue Bobs Overlap }
          Pal[ColorCounter OR 16 OR 32].Green := 21 + (ColorCounter * 6);
          Pal[ColorCounter OR 16 OR 32].Blue  := 21 + (ColorCounter * 6);

          { Make Colors Where Red, Green and Blue Bobs Overlap }
          Pal[ColorCounter OR 08 OR 16 OR 32].Red   := 21+(ColorCounter * 6);
          Pal[ColorCounter OR 08 OR 16 OR 32].Green := 21+(ColorCounter * 6);
          Pal[ColorCounter OR 08 OR 16 OR 32].blue  := 21+(ColorCounter * 6);
         End;

         { Make Colors Where The Grey Square Overlaps The Bobs }
         For ColorCounter := 128 to 255 do
         Begin
          Pal[ColorCounter].Red   := (Pal[ColorCounter-128].Red   DIV 4)+14;
          Pal[ColorCounter].Green := (Pal[ColorCounter-128].Green DIV 4)+14;
          Pal[ColorCounter].Blue  := (Pal[ColorCounter-128].Blue  DIV 4)+14;
         End;
    End;
    {-[ Draw Grey Square In Background Buffer ]-----------------------------}
    Procedure BuildBackground;
    Var Y, X : Integer;
    Begin
         FillChar (BackGround^, SizeOf(BackGround^), 0);

         For Y := 50 to 150 do
         For X := 100 to 220 do
             BackGround^[Y, X] := 128;

    End;
    {=[ Main Program ]======================================================}
    Begin
         VGA := Ptr ($A000,$0000);
         New (WorkPage);
         New (BackGround);

         InitializeVariables;
         BuildPalette;
         BuildBackground;

         SetMode13h;
         SetPalette (Pal);

         Repeat
               UpdateBobs;               { Update Bob Positions }
               WorkPage^ := BackGround^; { Clear WorkPage With Static Image }
               DrawBobs;                 { Draw Bobs }

               { Wait For Retrace }
               Repeat Until ((Port[$3DA] AND 8) <> 0);

               VGA^ := WorkPage^;        { Display Page }
         Until KeyPressed;

         TextMode (C80);

         Dispose (BackGround);
         Dispose (WorkPage);
    End.

    { PALETTE CODE FOLLOWS }

    {
     TD> I've seen it done in many places, but I haven't seen any info on
     TD> how it's done:  What is the basic algorithm for fading from one
     TD> palette to another.

            Many people do palette fading incorrectly.  The correct
    way to do it would be to set up a relation such as:

            Palette_Element     Calculated_Element
            ---------------  =  ------------------
             Max_Intensity      Current_Intensity

    Where Palette_Element is a single element in our master DAC
    table, Max_Intensity is the maximum allowable intensity level for
    our scale, Current_Intensity is a number between 0 and
    Max_Intensity which represents the level we want, and
    Calculated_Element is the new value for the element of our DAC
    table.  But since we want the Calculated_Element, we re-write it
    as this equation:

            Calculated_Element = Palette_Element * Current_Intensity
                                 -----------------------------------
                                             Max_Intensity

    The above equation will allow us to fade a given palette set to
    black or from black to a given palette set.  To fade out an entire
    palette set, you would need to calculate the above for the red,
    green, and blue components of each color in the 256 element DAC
    table.
            Fading from one palette set to another palette set is
    very similar.  What you must do is fade one palette set to black
    while simultaneously fade from black to another palette set and
    add the two values.  The equation for this is:

           CE = ((PE1 * (MI - CI)) + (PE2 * CI)) / MI

    Where CE is the calculated element, PE1 and PE2 are corresponding
    palette elements from palette 1 and 2, MI is the maximum
    intensity in our scale, and CI is the current intensity we want
    (num between 0 and MI). }

    Unit Palette;
    { Programmed By David Dahl @ FidoNet 1:272/38 }
    (* PUBLIC DOMAIN *)
    Interface
      Type PaletteRec = Record
                              Red   : Byte;
                              Green : Byte;
                              Blue  : Byte;
                        End;
           PaletteArray = Array [0..255] of PaletteRec;

      Procedure SetPalette (Var PaletteIn : PaletteArray);
      Procedure FadeFromPaletteToBlack (Var PaletteIn : PaletteArray);
      Procedure FadeFromBlackToPalette (Var PaletteIn : PaletteArray);
      Procedure FadeFromPalette1ToPalette2 (Var Palette1 : PaletteArray;
                                            Var Palette2 : PaletteArray);
    Implementation
    Procedure SetPalette (Var PaletteIn : PaletteArray); Assembler;
    Asm
       { Get Address of PaletteIn }
       LDS SI, PaletteIn
       CLD

       { Tell VGA To Start With First Palette Element }
       XOR AX, AX
       MOV DX, $3C8
       OUT DX, AL

       { Wait For Retrace }
       MOV DX, $3DA
       @VRWait1:
         IN AL, DX
         AND AL, 8
       JZ @VRWait1

       { Set First Half Of Palette }
       MOV DX, $3C9
       MOV CX, 128 * 3
       @PALLOOP1:
         LODSB  { DON'T use "REP OUTSB" since some VGA cards can't handle it }
         OUT DX, AL
       LOOP @PALLOOP1

       { Wait For Retrace }
       PUSH DX
       MOV DX, $3DA
       @VRWait2:
         IN AL, DX
         AND AL, 8
       JZ @VRWait2
       POP DX

       { Set Last Half Of Palette }
       MOV CX, 128 * 3
       @PALLOOP2:
         LODSB
         OUT DX, AL
       LOOP @PALLOOP2
    End;

    Procedure FadeFromPaletteToBlack (Var PaletteIn : PaletteArray);
    Var WorkPalette : PaletteArray;
        Counter     : Integer;
        Intensity   : Integer;
    Begin
         For Intensity := 31 downto 0 do
         Begin
           For Counter := 0 to 255 do
           Begin
              WorkPalette[Counter].Red   :=
                       (PaletteIn[Counter].Red   * Intensity) DIV 32;
              WorkPalette[Counter].Green :=
                       (PaletteIn[Counter].Green * Intensity) DIV 32;
              WorkPalette[Counter].Blue  :=
                       (PaletteIn[Counter].Blue  * Intensity) DIV 32;
           End;
           SetPalette (WorkPalette);
         End;
    End;

    Procedure FadeFromBlackToPalette (Var PaletteIn : PaletteArray);
    Var WorkPalette : PaletteArray;
        Counter     : Integer;
        Intensity   : Integer;
    Begin
         For Intensity := 1 to 32 do
         Begin
           For Counter := 0 to 255 do
           Begin
              WorkPalette[Counter].Red   :=
                       (PaletteIn[Counter].Red   * Intensity) DIV 32;
              WorkPalette[Counter].Green :=
                       (PaletteIn[Counter].Green * Intensity) DIV 32;
              WorkPalette[Counter].Blue  :=
                       (PaletteIn[Counter].Blue  * Intensity) DIV 32;
           End;
           SetPalette (WorkPalette);
         End;
    End;

    Procedure FadeFromPalette1ToPalette2 (Var Palette1 : PaletteArray;
                                          Var Palette2 : PaletteArray);
    Var WorkPalette : PaletteArray;
        Counter     : Integer;
        CrossFade   : Integer;
    Begin
         For CrossFade := 0 to 32 do
         Begin
           For Counter := 0 to 255 do
           Begin
             WorkPalette[Counter].Red   :=
                 ((Palette1[Counter].Red   * (32 - CrossFade)) +
                  (Palette2[Counter].Red   * CrossFade)) DIV 32;
             WorkPalette[Counter].Green :=
                 ((Palette1[Counter].Green * (32 - CrossFade)) +
                  (Palette2[Counter].Green * CrossFade)) DIV 32;
             WorkPalette[Counter].Blue  :=
                 ((Palette1[Counter].Blue  * (32 - CrossFade)) +
                  (Palette2[Counter].Blue  * CrossFade)) DIV 32;
           End;
           SetPalette (WorkPalette);
         End;
    End;
    End.

    TUTORIAL !!

            Transparent objects are rather simple.  What you do is
    set up your palette so pure colors are represented by powers of
    two.  This way you can "mix" your colors by ORing the values
    together.  For simplicity's sake, this example will use 3 colors:

            Bit  7 6 5 4 3 2 1 0
                           | | |
                           | | +----> Red
                           | +------> Green
                           +--------> Blue

    So now you would set your palette up as follows:

        All single colors:

          2^0 = 1   --   Red
          2^1 = 2   --   Green
          2^2 = 4   --   Blue

        All possible 2 color mixes:

          2^0 OR 2^1 = 1 OR 2 = 3   --   Red + Green  = Yellow
          2^0 OR 2^2 = 1 OR 4 = 5   --   Red + Blue   = Magenta
          2^1 OR 2^2 = 2 OR 4 = 6   --   Green + Blue = Cyan

        All possible 3 color mixes:

          2^0 OR 2^1 OR 2^2 = 1 OR 2 OR 4 = 7  --  R + G + B = White

    So our palette is set up as:

            0 - Black
            1 - Red
            2 - Green
            3 - Yellow
            4 - Blue
            5 - Magenta
            6 - Cyan
            7 - White

    Now let's say we have a Red, Green, and a Blue square.  The
    bitmap of the red square will be made up of bytes of the value 1,
    the green square will be made up of the value 2, and the blue
    square will be made up of the value 4 as so:

               Red             Green              Blue

             11111111         22222222          44444444
             11111111         22222222          44444444
             11111111         22222222          44444444
             11111111         22222222          44444444

    To put the squares, you just have to OR put them to your frame
    buffer.  If they overlap, they will automatically mix as so:

         The 3 overlaping bitmaps       The 3 overlaping bitmaps
         in frame buffer using an       in frame buffer showing
         OR'd image put:                what colors are where:

                11111111                      RRRRRRRR
                11111111                      RRRRRRRR
                111133332222                  RRRRYYYYGGGG
                155577776222                  RMMMWWWWCGGG
                 44466666222                   BBBCCCCCGGG
                 44466666222                   BBBCCCCCGGG
                 44444444                      BBBBBBBB

    The following example program uses this bit scheme:

            Bit  7 6 5 4 3 2 1 0
                 |   | | | +-+-+---> Color Intensity (0:Least - 7:Full)
                 |   | | +---------> Red
                 |   | +-----------> Green
                 |   +-------------> Blue
                 +-----------------> Grey

    David Dahl

[Back to EGAVGA SWAG index] [Back to Main SWAG index] [Original]