; BB.Plasma v1.2  Module for BlitzBlank
; Written by Daniel Pink

;--------------------------------------------------------------------

; The different kinds of GUI-objects for a module's config-window

#BB_PGroup=1     ; Indicates the start of a new page group
#BB_PGroup_End=2 ; Indicates the end of the page group
#BB_VGroup=3     ; Indicates the start of a new vertical group
#BB_VGroup_End=4 ; Indicates the end of the vertical group
#BB_Check=5      ; Checkmark, uses set field in BB_Object
#BB_String=6     ; Textgadget, uses max/contents fields in BB_Object
#BB_File=7       ; Stringgadget with attached filerequester
#BB_Slider=8     ; Slidergadget, uses min/max/set fields in BB_Object
#BB_Cycle=9      ; Cyclegadget, uses set/contents in BB_Object
#BB_Dummy=10     ; Dummy, if you want no gadgets at all
#BB_Font=11      ; Stringgadget with attached fontrequester
#BB_Dir=12       ; Stringgadget with attached filerequester, Dirs only

;--------------------------------------------------------------------

; Flags used in BB_Message's flag-field

#BBF_Screenmode=1 LSL  0   ; Allows screenmode-selection
#BBF_colours=1 LSL  1      ; Allows screendepth-selection
#BBF_Sample=1 LSL  2       ; Not supported yet
#BBF_NoWatch=1 LSL  3      ; Eats no CPU-time, so needs no checking
#BBF_NoScreen=1 LSL  4     ; Wants no screen from BlitzBlank
#BBF_FirstScreen=1 LSL  5  ; Wants pointer to screen in front, be CAREFUL with this!
#BBF_CloneScreen=1 LSL  6  ; Wants a clone from the FrontScreen
#BBF_AmigaOnly=1 LSL  7    ; Not supported yet
#BBF_NoMouseBlank=1 LSL  8 ; No mouseblanking from BlitzBlank wanted
#BBF_NoKeyPass=1 LSL  9    ; No keypassing necessary (no mouseblanking, no own active window
#BBF_BigWindow=1 LSL 10    ; The blankwindow fills the whole screen
#BBF_Interleaved=1 LSL 11  ; BlitzBlank TRIES to give you a screen with an Interleaved BitMap

;--------------------------------------------------------------------

; The structure, that holds config-data

NEWTYPE .BB_Object
  next_.l ;BB_Object ; Pointer to next object or NULL if last object
  type_.w            ; What kind of GUI-object this is
  min_.l             ; Minimum value for BB_Slider
  max_.l             ; Maximum value for BB_Slider, max length of BB_String
  set_.l             ; Value of BB_Slider, state of BB_Checkmark, BB_Cycle
  contents_.l        ; Pointer to buffer for BB_String, pointer to stringarray for BB_Cycle
  label_.l           ; Label for ALL objects
End NEWTYPE

;--------------------------------------------------------------------

; The message to send to BlitzBlank/BlitzBlankPrefs

NEWTYPE .BB_Message
  msg_.Message      ; Normal Exec-Message-structure
  flags_.l          ; Flags for this module
  infotext_.l       ; Pointer to infotext for this module
  *first_.BB_Object ; Pointer to first BB_Object or NULL for Info-action
  modpri_.w         ; not of use, if you use the library
  *path_.b          ; Path to directory for module-data
  *blitzblank_.Task ; not of use, if you use the library
End NEWTYPE

;--------------------------------------------------------------------

; The BlitzBlank-Screeninfo-structure

NEWTYPE .BB_Screeninfo
  xpos_.w           ; should be 0
  ypos_.w           ; should be 0
  width_.w          ; User-selected screen-width
  height_.w         ; User-selected screen-height
  depth_.w          ; User-selected screen-depth
  mode_.l           ; User-selected screen-mode
  *bbscreen_.Screen ; module screen
  *bbwindow_.Window ; blank window
  mindepth_.w       ; desired minimum depth or 0
  maxdepth_.w       ; desired maximum depth or 0
End NEWTYPE

;--------------------------------------------------------------------

; Flag-definition for BBL_AllocBitMap()/BBL_AllocRastPort,
; if you don't have the V39-includes

#BMB_CLEAR=0
#BMB_DISPLAYABLE=1
#BMB_INTERLEAVED=2
#BMF_CLEAR=1 LSL #BMB_CLEAR
#BMF_DISPLAYABLE=1 LSL #BMB_DISPLAYABLE
#BMF_INTERLEAVED=1 LSL #BMB_INTERLEAVED

;--------------------------------------------------------------------

; Locale string numbers
#PaletteStr1=600
#PaletteStr2=601
#PaletteStr3=602
#SpeedStr0=603
#SpeedStr1=604
#SpeedStr2=605
#SpeedStr3=606
#PaletteLabelStr=607
#SpeedLabelStr=608
#InfoStr=609
#BrightLabelStr=610

;--------------------------------------------------------------------

; Types for the AGA colourmap for LoadRGB23_
NEWTYPE .RGB
  _Red.l
  _Green.l
  _Blue.l
End NEWTYPE

NEWTYPE .ColourMap32
  _NumCols.w
  _Zero.w
  _RGB.RGB[256]
  _Zero2.l
End NEWTYPE

;--------------------------------------------------------------------

; Type for non AGA colourmap for LoadRGB4_
NEWTYPE .ColourMap4
  _NumCols.w
  _Colours.w[256]
End NEWTYPE

;--------------------------------------------------------------------

; Type for lookup table to see how many VWaits and colour
; cycles to do for different speeds and bitmap depths.
NEWTYPE .Update
  NoVWaits.b [7]
  NoCycles.b [7]
End NEWTYPE

NEWTYPE .Brightness
  Level39.b [5]
  LevelPre39.b [5]
End NEWTYPE

DEFTYPE .Brightness *BrightnessTable
DEFTYPE .Update *UpdateRate
DEFTYPE .ColourMap32 MyColourMap32
DEFTYPE .ColourMap4 MyColourMap4
*UpdateRate=?UpdateRateLUT
*BrightnessTable=?BrightnessLUT

;--------------------------------------------------------------------

; Set a colour in the colourmap
Statement SetColour{N.w,R.w,G.w,B.w}
SHARED MyColourMap32,MyColourMap4,NumColours.w,V39.b
  If N>=0 AND N<NumColours
    If V39
      MyColourMap32\_RGB[N]\_Red=R+(R LSL 8)+(R LSL 16)+(R LSL 24)
      MyColourMap32\_RGB[N]\_Green=G+(G LSL 8)+(G LSL 16)+(G LSL 24)
      MyColourMap32\_RGB[N]\_Blue=B+(B LSL 8)+(B LSL 16)+(B LSL 24)
    Else
      MyColourMap4\_Colours[N]=(R LSL 8)|(G LSL 4)|B
    EndIf
  EndIf
End Statement

;--------------------------------------------------------------------

; Initialise colourmap
Statement InitColourMap {}
SHARED MyColourMap32,MyColourMap4,NumColours,V39
  If V39
    MyColourMap32\_NumCols=NumColours
  Else
    MyColourMap4\_NumCols=NumColours
  EndIf
End Statement

;--------------------------------------------------------------------

; Show the colours in the current colourmap
Statement ShowColours {}
SHARED MyColourMap32,MyColourMap4,NumColours,vp.l,V39
  If V39
    LoadRGB32_ vp,&MyColourMap32
  Else
    LoadRGB4_ vp,&MyColourMap4,NumColours
  EndIf
End Statement

;--------------------------------------------------------------------

; Cycle the colours in the AGA colourmap
Statement CycleColours32 {PaletteAddress.l}
  MOVEM.l d0-d3,-(a7)
  MOVE.l d0,a0 ; a0 Points to .Palettedata
  MOVE.w (a0),d0 ; d0 is number of colours
  CMP.w #3,d0
  BLE ExitCycleColours32
  ADDA.l #16,a0
  MOVE.l (a0)+,d1 ; store the first palette entry
  MOVE.l (a0)+,d2 ; so that it can be put in the
  MOVE.l (a0)+,d3 ; last entry when we get there
  SUB.w #3,d0
CopyNext32:
  ; Copy palette entries to the previous entry
  MOVE.l (a0)+,-16(a0)
  MOVE.l (a0)+,-16(a0)
  MOVE.l (a0)+,-16(a0)
  DBRA d0,CopyNext32:
  SUBA #12,a0
  ; Copy the first entry to the last entry
  MOVE.l d1,(a0)+
  MOVE.l d2,(a0)+
  MOVE.l d3,(a0)
ExitCycleColours32:
  MOVEM.l (a7)+,d0-d3
  AsmExit
End Statement

;--------------------------------------------------------------------

; Cycle the colours in the non AGA colourmap
Statement CycleColours4 {PaletteAddress.l}
  MOVE.l d0,a0 ; a0 Points to .Palettedata
  MOVE.w (a0),d0 ; d0 is number of colours
  CMP.w #3,d0
  BLE ExitCycleColours4
  ADDA.l #2,a0
  MOVE.w (a0)+,d1 ; store the first palette entry
  SUB.w #3,d0
CopyNext4:
  ; Copy palette entries to the previous entry
  MOVE.w (a0)+,-4(a0)
  DBRA d0,CopyNext4:
  ; Copy the first entry to the last entry
  MOVE.w d1,-2(a0)
ExitCycleColours4:
  AsmExit
End Statement

;--------------------------------------------------------------------

Statement InitPlasmaPalette{}
SHARED NumColours.w,V39,Bright.b
ColsD3.w=NumColours/3
If V39
  SetColour{0,0,Int(Bright/ColsD3),Bright}
  For i=1 To ColsD3
    SetColour{i,0,Int(((i*Bright)/ColsD3)+0.5),Int(((ColsD3+1-i)*Bright)/ColsD3)}
    SetColour{i+ColsD3,Int((i*Bright)/ColsD3),Int(((ColsD3+1-i)*Bright)/ColsD3),0}
    SetColour{i+ColsD3+ColsD3,Int(((ColsD3+1-i)*Bright)/ColsD3),0,Int((i*Bright)/ColsD3)}
  Next i
Else
  SetColour{0,0,Int(Bright/ColsD3),Bright}
  For i=1 To ColsD3
    SetColour{i,0,Int((i*Bright)/ColsD3),Int(((ColsD3+1-i)*Bright)/ColsD3)}
    SetColour{i+ColsD3,Int((i*Bright)/ColsD3),Int(((ColsD3+1-i)*Bright)/ColsD3),0}
    SetColour{i+ColsD3+ColsD3,Int(((ColsD3+1-i)*Bright)/ColsD3),0,Int((i*Bright)/ColsD3)}
  Next i
EndIf
End Statement

;--------------------------------------------------------------------

Statement InitCloudPalette{}
SHARED NumColours,V39,Bright
ColsD2.w=NumColours/2
If V39
  For i=0 To NumColours
    SetColour{i,Int((Abs(i-ColsD2)*Bright)/ColsD2),Int((Abs(i-ColsD2)*Bright)/ColsD2),Bright}
  Next i
Else
  For i=0 To NumColours
    SetColour{i,Int(0.5+((Abs(i-ColsD2)*Bright)/ColsD2)),Int(0.5+(Abs(i-ColsD2)*Bright)/ColsD2),Bright}
  Next i
EndIf
End Statement

;--------------------------------------------------------------------

Statement InitElectricPalette {}
SHARED NumColours,V39,Bright
Threshold.w=NumColours/12
If V39
  For i=0 To NumColours
    If(i<=Threshold)
     SetColour{i,Int((((Threshold-i)/Threshold)*Bright)+0.5),Int((((Threshold-i)/Threshold)*Bright/3)+0.5),Bright/2}
    Else
      SetColour{i,0,0,0}
    EndIf
  Next i
Else
  If NumColours=32
    SetColour{0,Bright-1,Bright-2,Bright-2}
    SetColour{1,0,0,Bright/2}
    For i=2 To 32
      SetColour{i,0,0,0}
    Next i
  Else
    For i=0 To NumColours
      If(i<=Threshold)
        SetColour{i,Int(((Threshold-i)/Threshold)*Bright),Int(((Threshold-i)/Threshold)*Bright/2),Bright/2}
      Else
        SetColour{i,0,0,0}
      EndIf
    Next i
  EndIf
EndIf
End Statement

;--------------------------------------------------------------------

; Recursive procedure used to draw colours to a
; section of the bitmap bounded by (x1,y1) and (x2,y2)
Statement Sub_Divide{ x1.w, y1.w, x2.w, y2.w}
SHARED Exit.b,rp.l,NumColours
  If NOT(Exit)
    x.w=(x1+x2)/2
    y.w=(y1+y2)/2
    Col1.w=ReadPixel_(rp,x1,y1)
    Col2.w=ReadPixel_(rp,x2,y1)
    Col3.w=ReadPixel_(rp,x1,y2)
    Col4.w=ReadPixel_(rp,x2,y2)
    If ReadPixel_(rp,x,y1)=0
      Col.w=Abs(x1-x2)
      Col.w=Int(Rnd(Col*2)-Col)+(Col1+Col2)/2
      If (Col<1) Then Col=1 Else If (Col>NumColours) Then Col=NumColours
      SetAPen_ rp,Col
      WritePixel_ rp,x,y1
    EndIf
    If ReadPixel_(rp,x2,y)=0
      Col.w=Abs(y1-y2)
      Col.w=Int(Rnd(Col*2)-Col)+(Col2+Col4)/2
      If (Col<1) Then Col=1 Else If (Col>NumColours) Then Col=NumColours
      SetAPen_ rp,Col
      WritePixel_ rp,x2,y
    EndIf
    If ReadPixel_(rp,x,y2)=0
      Col.w=Abs(x1-x2)
      Col.w=Int(Rnd(Col*2)-Col)+(Col3+Col4)/2
      If (Col<1) Then Col=1 Else If (Col>NumColours) Then Col=NumColours
      SetAPen_ rp,Col
      WritePixel_ rp,x,y2
    EndIf
    If ReadPixel_(rp,x1,y)=0
      Col.w=Abs(y1-y2)
      Col.w=Int(Rnd(Col*2)-Col)+(Col1+Col3)/2
      If (Col<1) Then Col=1 Else If (Col>NumColours) Then Col=NumColours
      SetAPen_ rp,Col
      WritePixel_ rp,x1,y
    EndIf
    Col.w=(Col1+Col2+Col3+Col4+2)/4
    SetAPen_ rp,Col
    WritePixel_ rp,x,y
    Width1.b=x-x1>1
    Width2.b=x2-x>1
    Width3.b=y-y1>1
    Width4.b=y2-y>1
    If Width1 OR Width3 Then Sub_Divide{x1, y1, x,  y }
    If Width2 OR Width3 Then Sub_Divide{x,  y1, x2, y }
    If Width1 OR Width4 Then Sub_Divide{x1, y,  x,  y2}
    If Width2 OR Width4 Then Sub_Divide{x,  y,  x2, y2}
  End If
End Statement

;--------------------------------------------------------------------

; Data for palette cycle gadget
Dim PaletteCycle.l(3)
PaletteCycle(0)=BBL_GetString_ (#PaletteStr1,?_Palette1)
PaletteCycle(1)=BBL_GetString_ (#PaletteStr2,?_Palette2)
PaletteCycle(2)=BBL_GetString_ (#PaletteStr3,?_Palette3)
PaletteCycle(3)=0

;--------------------------------------------------------------------

; Data for cycle speed cycle gadget
Dim SpeedCycle.l(4)
SpeedCycle(0)=BBL_GetString_ (#SpeedStr0,?Speed0)
SpeedCycle(1)=BBL_GetString_ (#SpeedStr1,?Speed1)
SpeedCycle(2)=BBL_GetString_ (#SpeedStr2,?Speed2)
SpeedCycle(3)=BBL_GetString_ (#SpeedStr3,?Speed3)
SpeedCycle(4)=0

;--------------------------------------------------------------------

; MUI objects for preferences
Dim Objects.BB_Object(4)
Objects(0)\next_=&Objects(1),#BB_VGroup,0,0,0,0,0
Objects(1)\next_=&Objects(2),#BB_Cycle,0,0,0,&PaletteCycle(0),BBL_GetString_(#PaletteLabelStr,?PaletteLabel)
Objects(2)\next_=&Objects(3),#BB_Cycle,0,0,0,&SpeedCycle(0),BBL_GetString_(#SpeedLabelStr,?SpeedLabel)
Objects(3)\next_=&Objects(4),#BB_Slider,0,4,0,0,BBL_GetString_(#BrightLabelStr,?BrightLabel)
Objects(4)\next_=0,#BB_VGroup_End,0,0,0,0,0

DEFTYPE .BB_Message Message
DEFTYPE .BB_Screeninfo *ScreenInfo
DEFTYPE .Screen *MyScreen

;--------------------------------------------------------------------

; Construct message to return to BlitzBlank
Message\first_=&Objects(0)
Message\infotext_=BBL_GetString_(#InfoStr,?InfoMessage)
Message\flags_=#BBF_Screenmode+#BBF_colours

;--------------------------------------------------------------------

; Get pointer to screeninfo
StrToLong_ Par$(3),&*ScreenInfo
*ScreenInfo\mindepth_=5

;--------------------------------------------------------------------

; Check which action to do
Select Par$(1)
Case "BLANK"
  BBL_SendMessage_ &Message,Par$(2)
  *MyScreen=*ScreenInfo\bbscreen_
  If *MyScreen<>0
    ScreenToFront_ *MyScreen
    BBL_ModuleRunning_
    Speed.b=*ScreenInfo\depth_-(Objects(2)\set_+2)
    Gosub SetInterrupt ; Interrupt used to stop while drawing
    vp.l=*MyScreen\_ViewPort
    rp.l=*MyScreen\_RastPort
    Choice.b=Objects(1)\set_
    NumColours.w=1LSL*ScreenInfo\depth_
    Bright.b=Objects(3)\set_

    If ExecVersion=39
      V39=True
    Else
      V39=False
    EndIf

    If V39
      Bright=*BrightnessTable\Level39[Bright]
    Else
      Bright=*BrightnessTable\LevelPre39[Bright]
    EndIf

    InitColourMap {}
    Select Choice.b
    Case 1
      InitCloudPalette{}
    Case 2
      InitElectricPalette{}
    Default
      InitPlasmaPalette{}
    End Select
    ShowColours {}
    SetAPen_ rp,Int(Rnd(NumColours)+1)
    WritePixel_ rp,0,0
    SetAPen_ rp,Int(Rnd(NumColours)+1)
    WritePixel_ rp,*ScreenInfo\width_-1, 0
    SetAPen_ rp,Int(Rnd(NumColours)+1)
    WritePixel_ rp,*ScreenInfo\width_-1, *ScreenInfo\height_-1
    SetAPen_ rp,Int(Rnd(NumColours)+1)
    WritePixel_ rp,0, *ScreenInfo\height_-1
    Sub_Divide{0, 0, *ScreenInfo\width_-1, *ScreenInfo\height_-1}
    ClrInt 5 ; Finished drawing, so don't need interrupt
    If NOT Exit
      While CheckSignal_(#SIGBREAKF_CTRL_C)<>#SIGBREAKF_CTRL_C
        For Counter.b=1 To *UpdateRate\NoVWaits[Speed]
          WaitTOF_
        Next Counter
        For Counter=1 To *UpdateRate\NoCycles[Speed]
          If V39
            CycleColours32 {&MyColourMap32}
          Else
            CycleColours4 {&MyColourMap4}
          EndIf
        Next Counter
        ShowColours{}
      Wend
    EndIf
  EndIf
  BBL_BlankDone_
Case "CONFIG"
  BBL_SendMessage_ &Message,Par$(2)
Case "INFO"
  Message\first_=0
  BBL_SendMessage_ &Message,Par$(2)
End Select

End

;--------------------------------------------------------------------

.SetInterrupt
Exit.b=False
SetInt 5
  If CheckSignal_(#SIGBREAKF_CTRL_C)=#SIGBREAKF_CTRL_C Then Exit=True
End SetInt
Return

;--------------------------------------------------------------------

; Default strings
._Palette1:
Dc.b "Plasma",0

._Palette2:
Dc.b "Clouds",0

._Palette3:
Dc.b "Electrical",0

.PaletteLabel:
Dc.b "Palette T_ype",0

.SpeedLabel:
Dc.b "Colour Cycle _Rate",0

.Speed0:
Dc.b "Very Fast",0

.Speed1:
Dc.b "Fast",0

.Speed2:
Dc.b "Medium",0

.Speed3:
Dc.b "Slow",0

.InfoMessage:
Dc.b 27,"c",27,"bPlasma",27,"n v1.2",10,10
Dc.b "Module for BlitzBlank",10,10
Dc.b "Copyright 1995",10
Dc.b "By",10
Dc.b "Daniel Pink",10,0

.BrightLabel:
Dc.b "_Brightness",0
;--------------------------------------------------------------------


; Lookup table for number of VWaits and Cycles
.UpdateRateLUT
Dc.b  8, 5, 3, 2, 1, 1, 1
Dc.b  1, 1, 1, 1, 1, 2, 3

.BrightnessLUT
Dc.b 63,80,96,112,127
Dc.b 4,5,6,7,8

Even
