Jump to content
Sign in to follow this  
adamski

GDI+ Colour Mixing Modes

Recommended Posts

adamski

Is there a way to control the way overlapping colours are mixed with GDI+. For example: A green(00FF00) box overlaps a red(FF0000) box with the overlap being yellow(FFFF00) ie. the colours are added. I know for example this I could just draw a yellow box, but I want to use gradient fills and opacity which complicates it.

I think maybe something like this is possible using GDI Raster Ops and BitBlt but I don't know how to use them. Any examples would be greatly apreciated.

Thanks.

#include <GUIConstants.au3>
#include <GDIplus.au3>

; Build your GUI here
Opt("GUIOnEventMode",1)

Global Const $GUIWidth=600
Global Const $GUIHeight=400
GLobal $GUITitle="GDI+"

$hwnd=GUICreate($GUITitle,$GUIWidth,$GUIHeight)
GUISetOnEvent($GUI_EVENT_CLOSE,"close")
$GDILabel=GUICtrlCreateLabel("",50,50,500,300)
GUISetState()

; Load your GDI+ resources here:
_GDIPlus_Startup()
$graphics=_GDIPlus_GraphicsCreateFromHWND(ControlGetHandle($hwnd,"",$GDILabel))
$bitmap=_GDIPlus_BitmapCreateFromGraphics($GUIWidth,$GUIHeight,$graphics)
$backbuffer=_GDIPlus_ImageGetGraphicsContext($bitmap)

; Size
Local $SquareWidth = 300, $SquareHeight = 200

; Brush Data
$hBrushRed  = _GDIPlus_BrushCreateSolid(0xFFFF0000)
$hBrushBlue = _GDIPlus_BrushCreateSolid(0xFF00FF00)

Do
    _GDIPlus_GraphicsClear($backbuffer)

; Draw Two Boxes
    _GDIPlus_GraphicsFillRect($backbuffer, 10, 10, $SquareWidth, $SquareHeight, $hBrushRed)
    _GDIPlus_GraphicsFillRect($backbuffer, 190, 90, $SquareWidth, $SquareHeight, $hBrushBlue)

    _GDIPlus_GraphicsDrawImageRect($graphics,$bitmap,0,0,$GUIWidth,$GUIHeight)
    Sleep(10)
Until False

Func close()
    _GDIPlus_GraphicsDispose($backbuffer)
    _GDIPlus_BitmapDispose($bitmap)
    _GDIPlus_GraphicsDispose($graphics)
    _GDIPlus_Shutdown()
    Exit
EndFunc

Share this post


Link to post
Share on other sites
monoceres

Probably a stupid question, but you have played around with the alpha channel in the brushes right?


Broken link? PM me and I'll send you the file!

Share this post


Link to post
Share on other sites
adamski

Probably a stupid question, but you have played around with the alpha channel in the brushes right?

Yes I have but changing the alpha produces a different effect from what I am after. I want the final colour to have the red component of one object, the green component of another, and the blue of the background with the ability to have the colours as gradients. I can do it pixel by pixel but that is very slow.

Share this post


Link to post
Share on other sites
Malkey

Is there a way to control the way overlapping colours are mixed with GDI+. For example: A green(00FF00) box overlaps a red(FF0000) box with the overlap being yellow(FFFF00) ie. the colours are added. I know for example this I could just draw a yellow box, but I want to use gradient fills and opacity which complicates it.

I think maybe something like this is possible using GDI Raster Ops and BitBlt but I don't know how to use them. Any examples would be greatly apreciated.

Thanks.

Here is an example allowing experimenting with $iROP (Raster Ops) found at the _WinAPI_BitBlt() function in the help file.

The BitBlt function is from the GDI32.dll file. So the colour format is 0xRRGGBB, no alpha channel.

This is a cut down modified version of the example found at

http://www.autoitscript.com/forum/index.ph...st&p=561542

Comment and un-comment different _WinAPI_BitBlt() functions in the script to see the result. Or, add your own _WinAPI_BitBlt() with a different $iROP parameter.

#include <WinAPI.au3>
#include <GUIConstants.au3>
#include <WindowsConstants.au3>

; Modified from http://www.autoitscript.com/forum/index.php?s=&showtopic=77580&view=findpost&p=561542
Global $iX = 0, $iY = 0, $iWidth = 600, $iHeight = 400, $iHeight, $iW

BitmapMasking( 0, 0, $iWidth, $iHeight)

While GUIGetMsg() <> -3
    Sleep(10)
WEnd

Func BitmapMasking( $iX = 0, $iY = 0, $iWidth = 200, $iHeight = 200)
    Local $hWnd, $hDDC, $hCDC, $hBMP, $aIcon, $hIcon

; Create GUI for Green
    $GUIGreen = GUICreate("Green", $iWidth, $iHeight, 0, 0)
    WinSetTrans("Green", "", 255)
    GUISetBkColor(0x00ff00, $GUIGreen);green
    GUISetState()
    $hDGrn1 = _WinAPI_GetDC($GUIGreen)
    $hCGrn1 = _WinAPI_CreateCompatibleDC($hDGrn1)
    $hBMP1 = _WinAPI_CreateCompatibleBitmap($hDGrn1, $iWidth,  $iHeight)
    _WinAPI_SelectObject($hCGrn1, $hBMP1)

; Create GUI for Red
    $hGUI = GUICreate("Red", $iWidth, $iHeight, -1, @DesktopHeight / 2)
    WinSetTrans("Red", "", 255)
    GUISetBkColor(0xff0000, $hGUI);red
    GUISetState()
    $hDRed2 = _WinAPI_GetDC($hGUI)
    $hCRed2 = _WinAPI_CreateCompatibleDC($hDRed2)
    $hBMP2 = _WinAPI_CreateCompatibleBitmap($hDRed2, $iWidth,  $iHeight)
    _WinAPI_SelectObject($hCRed2, $hBMP2)

; Create GUI for result
    $hGUIRes = GUICreate("Result", $iWidth, $iHeight, @DesktopWidth / 2, 10)
    WinSetTrans("Result", "", 255)
    GUISetBkColor(0xFFFFFF, $hGUIRes); white
    GUISetState()
    $hDRes3 = _WinAPI_GetDC($hGUIRes)
    $hCRes3 = _WinAPI_CreateCompatibleDC($hDRes3)
    $hBMP3 = _WinAPI_CreateCompatibleBitmap($hDRes3, $iWidth,  $iHeight)
    _WinAPI_SelectObject($hCRes3, $hBMP3)

;========= Experiment here with $iROP (Raster Ops) see _WinAPI_BitBlt() in help file ========

;$NOTSRCERASE - Combines the colors of the source and destination rectangles by using the OR operator and
;then inverts the resultant color
;Red and green combined = 0xFF0000 and 0x00FF00 = 0xFFFF00 inverted = 0x0000FF blue
;_WinAPI_BitBlt($hDRes3, 0, 0, $iWidth,  $iHeight, $hDRed2, $iX, $iY, $SRCCOPY )   ; Direct copy red
;_WinAPI_BitBlt($hDRes3, 0, 0, $iWidth,  $iHeight, $hDGrn1, $iX, $iY, $NOTSRCERASE )  ; OR operator

; Result is light blue
;_WinAPI_BitBlt($hDRes3, 0, 0, $iWidth,  $iHeight, $hDRed2, $iX, $iY, $SRCAND)  ; AND operator
;_WinAPI_BitBlt($hDRes3, 0, 0, $iWidth,  $iHeight, $hDGrn1, $iX, $iY, $PATINVERT) ; XOR operator

; Result is yellow
    _WinAPI_BitBlt($hDRes3, 0, 0, $iWidth,  $iHeight, $hDRed2, $iX, $iY, $SRCAND); AND operator
    _WinAPI_BitBlt($hDRes3, 0, 0, $iWidth,  $iHeight, $hDGrn1, $iX, $iY, $SRCPAINT); OR operator
    
; ===============================
    
    _WinAPI_ReleaseDC($GUIGreen, $hDGrn1)
    _WinAPI_DeleteDC($hCGrn1)
    _WinAPI_ReleaseDC($hGUI, $hDRed2)
    _WinAPI_DeleteDC($hCRed2)
    _WinAPI_ReleaseDC($hGUIRes, $hDRes3)
    _WinAPI_DeleteDC($hCRes3)
    _WinAPI_DeleteObject($hBMP1)
    _WinAPI_DeleteObject($hBMP2)
    _WinAPI_DeleteObject($hBMP3)
EndFunc  ;==>BitmapMasking

I can't see you using Raster Ops for what you want to do.

I hope I'm wrong. I might learn something.

Share this post


Link to post
Share on other sites
adamski

I can't see you using Raster Ops for what you want to do.

I hope I'm wrong. I might learn something.

Thanks for the example code. I have it working - kinda.

Edit: I think I have this done now - code updated

I would like to have the raster op result drawn to a buffer to reduce flickering but I have been unable to figure out how to do it. I have it working for 3 of 4 boxes but I'm not to hot on device contexts which is where it is going wrong I think.

Additionally, I would like to hide the three "working boxes" and just have the final result dissplayed. Is that possible?

This code is a bit long, but that is mainly due to the functions at the end for gradient fills.

#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <GUIConstants.au3>
#include <GDIplus.au3>

; Build your GUI here
Opt("GUIOnEventMode",1)

; Box Size
Global Const $RectWidth = 250
Global Const $RectHeight = 200

Global Const $GUIWidth=30+2*$RectWidth
Global Const $GUIHeight=30+2*$RectHeight
GLobal $GUITitle="GDI+"

$hwnd=GUICreate($GUITitle,$GUIWidth,$GUIHeight)
GUISetOnEvent($GUI_EVENT_CLOSE,"close")

$GDILabel1=GUICtrlCreateLabel("",10,10,$RectWidth,$RectHeight)
$GDILabel2=GUICtrlCreateLabel("",20 + $RectWidth,10,$RectWidth,$RectHeight)
$GDILabel3=GUICtrlCreateLabel("",10,20 + $RectHeight,$RectWidth,$RectHeight)
$GDILabel4=GUICtrlCreateLabel("",20 + $RectWidth,20 + $RectHeight,$RectWidth,$RectHeight)
GUISetState()

; Load your GDI+ resources here:
_GDIPlus_Startup()
$graphics1=_GDIPlus_GraphicsCreateFromHWND(ControlGetHandle($hwnd,"",$GDILabel1))
$bitmap1=_GDIPlus_BitmapCreateFromGraphics($GUIWidth,$GUIHeight,$graphics1)
$backbuffer1=_GDIPlus_ImageGetGraphicsContext($bitmap1)

$graphics2=_GDIPlus_GraphicsCreateFromHWND(ControlGetHandle($hwnd,"",$GDILabel2))
$bitmap2=_GDIPlus_BitmapCreateFromGraphics($GUIWidth,$GUIHeight,$graphics2)
$backbuffer2=_GDIPlus_ImageGetGraphicsContext($bitmap2)

$graphics3=_GDIPlus_GraphicsCreateFromHWND(ControlGetHandle($hwnd,"",$GDILabel3))
$bitmap3=_GDIPlus_BitmapCreateFromGraphics($GUIWidth,$GUIHeight,$graphics3)
$backbuffer3=_GDIPlus_ImageGetGraphicsContext($bitmap3)

;~ $graphics4=_GDIPlus_GraphicsCreateFromHWND(ControlGetHandle($hwnd,"",$GDILabel4))
;~ $bitmap4=_GDIPlus_BitmapCreateFromGraphics($GUIWidth,$GUIHeight,$graphics4)
;~ $backbuffer4=_GDIPlus_ImageGetGraphicsContext($bitmap4)

Local Const $iHorizontal = 0x00000000, $iVertical = 0x00000001

; Blends
Local $aBlends[2] = [0xFF008040, 0xFF0000C0]

; Brushes
$hBrushGradH = _CreateGradientBrush($aBlends, 0, 0, $RectWidth, $RectHeight, $iHorizontal)
$hBrushGreen = _GDIPlus_BrushCreateSolid(0xFF008000)

; For Red Brush
Local $inc = 0

; Set Up Handels for Raster Ops
$hD1 = _WinAPI_GetDC(ControlGetHandle($hwnd,"",$GDILabel1))
$hD2 = _WinAPI_GetDC(ControlGetHandle($hwnd,"",$GDILabel2))
$hD3 = _WinAPI_GetDC(ControlGetHandle($hwnd,"",$GDILabel3))
$hD4 = _WinAPI_GetDC(ControlGetHandle($hwnd,"",$GDILabel4))
$backbuffer4 = _WinAPI_CreateCompatibleDC($hD4)
$graphics4 = _WinAPI_CreateCompatibleBitmap($hD4, $RectWidth, $RectHeight)
_WinAPI_SelectObject($backbuffer4, $graphics4)
    
Do
    _GDIPlus_GraphicsClear($backbuffer1)
    _GDIPlus_GraphicsClear($backbuffer2)
    _GDIPlus_GraphicsClear($backbuffer3)
;~   _GDIPlus_GraphicsClear($backbuffer4)

; Shades of Red
    $inc += 0.1
    $CurrentColour = "0xFF" & Hex(((Sin(1 * $inc / 1) + 1) / 2) * 256, 2) & "0000"
    $hBrushChange = _GDIPlus_BrushCreateSolid($CurrentColour)
    
; Draw Three Boxes
    _GDIPlus_GraphicsFillRect($backbuffer1, 0, 0, $RectWidth, $RectHeight, $hBrushChange)
    _GDIPlus_GraphicsFillRect($backbuffer2, 0, 0, $RectWidth, $RectHeight, $hBrushGreen)
    _GDIPlus_GraphicsFillRect($backbuffer3, 0, 0, $RectWidth, $RectHeight, $hBrushGradH)

; Draw Raster Result Box
    _WinAPI_BitBlt($backbuffer4, 0, 0, $RectWidth,  $RectHeight, $hD1, 0, 0, $SRCCOPY); COPY operator
    _WinAPI_BitBlt($backbuffer4, $RectWidth/4, 0, $RectWidth/2,  $RectHeight, $hD2, 0, 0, $SRCPAINT); OR operator
    _WinAPI_BitBlt($backbuffer4, 0, 0, $RectWidth,  $RectHeight/2, $hD3, 0, 0, $SRCPAINT); OR operator

; Draw Buffers to Screen
    _GDIPlus_GraphicsDrawImageRect($graphics1,$bitmap1,0,0,$GUIWidth,$GUIHeight)
    _GDIPlus_GraphicsDrawImageRect($graphics2,$bitmap2,0,0,$GUIWidth,$GUIHeight)
    _GDIPlus_GraphicsDrawImageRect($graphics3,$bitmap3,0,0,$GUIWidth,$GUIHeight)
    _WinAPI_BitBlt($hD4, 0, 0, $RectWidth,  $RectHeight, $backbuffer4, 0, 0, $SRCCOPY); COPY operator
    
;~   Sleep(10)
Until False

Func _CreateGradientBrush($aBlends, $xPos, $yPos, $Width, $Height, $iDirection = 0x00000000, $aPositions = "")

    If Not IsArray($aPositions) Then
        Local $BlendsDim = UBound($aBlends)
        Dim $aPositions[$BlendsDim]
        For $i=0 To $BlendsDim -1
            $aPositions[$i] = 1/($BlendsDim-1)*$i
        Next
    EndIf
        
    $hBrushLin = _GDIPlus_CreateLineBrushFromRect($xPos, $yPos, $Width, $Height, $aPositions, $aPositions, $aBlends[0], $aBlends[UBound($aBlends)-1], $iDirection)
    _GDIPlus_SetLinePresetBlend($hBrushLin, $aBlends, $aPositions)

    Return $hBrushLin

EndFunc

;====_GDIPlus_CreateLineBrushFromRect === Malkey's function
;Description - Creates a LinearGradientBrush object from a set of boundary points and boundary colors.
; $aFactors - If non-array, default array will be used.
;          Pointer to an array of real numbers that specify blend factors. Each number in the array
;          specifies a percentage of the ending color and should be in the range from 0.0 through 1.0.
;$aPositions - If non-array, default array will be used.
;           Pointer to an array of real numbers that specify blend factors' positions. Each number in the array
;           indicates a percentage of the distance between the starting boundary and the ending boundary
;           and is in the range from 0.0 through 1.0, where 0.0 indicates the starting boundary of the
;           gradient and 1.0 indicates the ending boundary. There must be at least two positions
;           specified: the first position, which is always 0.0, and the last position, which is always
;           1.0. Otherwise, the behavior is undefined. A blend position between 0.0 and 1.0 indicates a
;           line, parallel to the boundary lines, that is a certain fraction of the distance from the
;           starting boundary to the ending boundary. For example, a blend position of 0.7 indicates
;           the line that is 70 percent of the distance from the starting boundary to the ending boundary.
;           The color is constant on lines that are parallel to the boundary lines.
; $iArgb1   - First Top color in 0xAARRGGBB format
; $iArgb2   - Second color in 0xAARRGGBB format
; $LinearGradientMode -  LinearGradientModeHorizontal      = 0x00000000,
;                       LinearGradientModeVertical       = 0x00000001,
;                       LinearGradientModeForwardDiagonal  = 0x00000002,
;                       LinearGradientModeBackwardDiagonal = 0x00000003
; $WrapMode  - WrapModeTile    = 0,
;             WrapModeTileFlipX  = 1,
;             WrapModeTileFlipY  = 2,
;             WrapModeTileFlipXY = 3,
;             WrapModeClamp   = 4
; GdipCreateLineBrushFromRect(GDIPCONST GpRectF* rect, ARGB color1, ARGB color2,
;            LinearGradientMode mode, GpWrapMode wrapMode, GpLineGradient **lineGradient)
; Reference:  http://msdn.microsoft.com/en-us/library/ms534043(VS.85).aspx

Func _GDIPlus_CreateLineBrushFromRect($iX, $iY, $iWidth, $iHeight, $aFactors, $aPositions, _
        $iArgb1 = 0xFF0000FF, $iArgb2 = 0xFFFF0000, $LinearGradientMode = 0x00000001, $WrapMode = 0)

    Local $tRect, $pRect, $aRet, $tFactors, $pFactors, $tPositions, $pPositions, $iCount

    If $iArgb1 = Default Then $iArgb1 = 0xFF0000FF
    If $iArgb2 = Default Then $iArgb2 = 0xFFFF0000
    If $LinearGradientMode = -1 Or $LinearGradientMode = Default Then $LinearGradientMode = 0x00000001
    If $WrapMode = -1 Or $LinearGradientMode = Default Then $WrapMode = 1

    $tRect = DllStructCreate("float X;float Y;float Width;float Height")
    $pRect = DllStructGetPtr($tRect)
    DllStructSetData($tRect, "X", $iX)
    DllStructSetData($tRect, "Y", $iY)
    DllStructSetData($tRect, "Width", $iWidth)
    DllStructSetData($tRect, "Height", $iHeight)

   ;Note: Withn _GDIPlus_Startup(), $ghGDIPDll is defined
    $aRet = DllCall($ghGDIPDll, "int", "GdipCreateLineBrushFromRect", "ptr", $pRect, "int", $iArgb1, _
            "int", $iArgb2, "int", $LinearGradientMode, "int", $WrapMode, "int*", 0)

    If IsArray($aFactors) = 0 Then Dim $aFactors[4] = [0.0, 0.4, 0.6, 1.0]
    If IsArray($aPositions) = 0 Then Dim $aPositions[4] = [0.0, 0.3, 0.7, 1.0]

    $iCount = UBound($aPositions)
    $tFactors = DllStructCreate("float[" & $iCount & "]")
    $pFactors = DllStructGetPtr($tFactors)
    For $iI = 0 To $iCount - 1
        DllStructSetData($tFactors, 1, $aFactors[$iI], $iI + 1)
    Next
    $tPositions = DllStructCreate("float[" & $iCount & "]")
    $pPositions = DllStructGetPtr($tPositions)
    For $iI = 0 To $iCount - 1
        DllStructSetData($tPositions, 1, $aPositions[$iI], $iI + 1)
    Next

    $hStatus = DllCall($ghGDIPDll, "int", "GdipSetLineBlend", "hwnd", $aRet[6], _
            "ptr", $pFactors, "ptr", $pPositions, "int", $iCount)
    Return $aRet[6]; Handle of Line Brush
EndFunc  ;==>_GDIPlus_CreateLineBrushFromRect

;===========================================================
; Description:  Sets the colors to be interpolated for this linear gradient brush and their
;           corresponding blend positions.
; Parameters
; $hBrush   [in] Pointer to the LinearGradientBrush object.
; $aBlend   [in] Pointer to an array of ARGB colors that specify the colors to be interpolated
;           for this linear gradient brush. A color of a given index in the blend array
;           corresponds to the blend position of that same index in the positions array.
; $aPositions [in] Pointer to an array of real numbers that specify the blend positions.
;              Each number in the array specifies a percentage of the distance between
;              the starting boundary and the ending boundary and is in the range from
;              0.0 through 1.0, where 0.0 indicates the starting boundary of the gradient
;              and 1.0 indicates the ending boundary. There must be at least two positions
;              specified: the first position, which is always 0.0f, and the last position,
;              which is always 1.0f. Otherwise, the behavior is undefined. A blend position
;              between 0.0 and 1.0 indicates the line, parallel to the boundary lines,
;              that is a certain fraction of the distance from the starting boundary to the
;              ending boundary. For example, a blend position of 0.7 indicates the line that
;              is 70 percent of the distance from the starting boundary to the ending boundary.
;              The color is constant on lines that are parallel to the boundary lines.
; GdipSetLinePresetBlend(GpLineGradient *brush, GDIPCONST ARGB *blend, GDIPCONST REAL* positions, INT count)
; Reference:  http://msdn.microsoft.com/en-us/library/ms534043(VS.85).aspx

Func _GDIPlus_SetLinePresetBlend($hBrush, $aBlend = 0, $aPositions = 0)
    Local $iCount, $tBlend, $pBlend, $tPositions, $pPositions, $hStatus, $res
   
   ;                                      Red,    Yellow,     Green,      Blue
    If IsArray($aBlend) = 0 Then Dim $aBlend[4] = [0xFFFF0000, 0xffffff00, 0xFF00FF00, 0xFF0000FF]
    If IsArray($aPositions) = 0 Then Dim $aPositions[4] = [0.0, 0.3, 0.7, 1.0]

    $iCount = UBound($aPositions)
    $tBlend = DllStructCreate("int[" & $iCount & "]")
    $pBlend = DllStructGetPtr($tBlend)
    For $iI = 0 To $iCount - 1
        DllStructSetData($tBlend, 1, $aBlend[$iI], $iI + 1)
    Next
    $tPositions = DllStructCreate("float[" & $iCount & "]")
    $pPositions = DllStructGetPtr($tPositions)
    For $iI = 0 To $iCount - 1
        DllStructSetData($tPositions, 1, $aPositions[$iI], $iI + 1)
    Next

    $hStatus = DllCall($ghGDIPDll, "int", "GdipSetLinePresetBlend", "hwnd", $hBrush, _
            "int", $pBlend, "ptr", $pPositions, "int", $iCount)
    Return
EndFunc  ;==>_GDIPlus_SetLinePresetBlend

Func close()
    _GDIPlus_GraphicsDispose($backbuffer1)
    _GDIPlus_BitmapDispose($bitmap1)
    _GDIPlus_GraphicsDispose($graphics1)
    _GDIPlus_GraphicsDispose($backbuffer2)
    _GDIPlus_BitmapDispose($bitmap2)
    _GDIPlus_GraphicsDispose($graphics2)
    _GDIPlus_GraphicsDispose($backbuffer3)
    _GDIPlus_BitmapDispose($bitmap3)
    _GDIPlus_GraphicsDispose($graphics3)
    
;~   _WinAPI_ReleaseDC($hwnd, $hD1)
;~   _WinAPI_DeleteDC($hD1)
;~   _WinAPI_ReleaseDC($hwnd, $hD2)
;~   _WinAPI_DeleteDC($hD2)
;~   _WinAPI_ReleaseDC($hwnd, $hD3)
;~   _WinAPI_DeleteDC($hD3)
;~   _WinAPI_ReleaseDC($hwnd, $hD4)
;~   _WinAPI_DeleteDC($hD4)
    
    _GDIPlus_Shutdown()
    Exit
EndFunc
Edited by adamski

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×