Demo of Cairo's compositing operators

Linux specific forum
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Demo of Cairo's compositing operators

Post by Shardik »

The following code demonstrates the effect of Cairo's compositing operators when combining two different color planes.

The Cairo library is built-in in all Linux distributions, so you don't have to install any additional libraries. GTK+ started in 2005 with version 2.8 to use Cairo to render the majority of its widgets.
wikipedia wrote:Cairo is a software library used to provide a vector graphics-based, device-independent API for software developers. It is designed to provide primitives for 2-dimensional drawing across a number of different backends. Cairo is designed to use hardware acceleration[1] when available.

Code: Select all

; Description and resulting images of composing operators:
; http://cairographics.org/operators

EnableExplicit

#CAIRO_FORMAT_ARGB32 = 0
#CAIRO_OPERATOR_CLEAR = 0
#CAIRO_OPERATOR_SOURCE = 1
#CAIRO_OPERATOR_OVER = 2

ImportC ""
  gdk_cairo_create(*Drawable.GdkDrawable)
  gdk_cairo_region(*CairoContext, *Drawable.GdkDrawable)
  gtk_widget_set_mapped(*Widget.GtkWidget)
EndImport

ImportC "-lcairo"
  cairo_create(*CairoSurface)
  cairo_destroy(*CairoContext)
  cairo_fill(*CairoContext)
  cairo_image_surface_create(CairoFormat.I, Width.I, Height.I)
  cairo_line_to(*CairoContext, x.D, y.D)
  cairo_move_to(*CairoContext, x.D, y.D)
  cairo_rectangle(*CairoContext, x.D, y.D, Width.D, Height.D)
  cairo_paint(*CairoContext)
  cairo_set_dash(*CairoContext, *DashArray, NumDashes.I, PatternStartOffset.D)
  cairo_set_line_width(*CairoContext, LineWidth.D)
  cairo_set_operator(*CairoContext, OperatorName.I)
  cairo_set_source_rgba(*CairoContext, Red.D, Green.D, Blue.D, Alpha.D)
  cairo_set_source_surface(*CairoContext, *CairoSurface, x.D, y.D)
  cairo_stroke(*CairoContext)
  cairo_surface_destroy(*CairoSurface)
EndImport

Enumeration
  #TextOperatorName
  #EditorOperatorDescription
  #ButtonPrevious
  #ButtonNext
EndEnumeration

DataSection
  Data.S "CAIRO_OPERATOR_CLEAR"
  Data.S "Where the second object is drawn, the first is completely removed. Anywhere else it is left intact. The second object itself is not drawn."
  Data.S "CAIRO_OPERATOR_SOURCE"
  Data.S "The second object is drawn as if nothing else were below. Only outside of the blue rectangle the red one is left intact."
  Data.S "CAIRO_OPERATOR_OVER"
  Data.S "The image shows what you would expect if you held two semi-transparent slides on top of each other. This operator is cairo's default operator."
  Data.S "CAIRO_OPERATOR_IN"
  Data.S "The first object is removed completely, the second is only drawn where the first was."
  Data.S "CAIRO_OPERATOR_OUT"
  Data.S "The blue rectangle is drawn only where the red one wasn't. Since the red one was partially transparent, you can see a blue shadow in the overlapping area. Otherwise, the red object is completely removed."
  Data.S "CAIRO_OPERATOR_ATOP"
  Data.S "This leaves the first object mostly intact, but mixes both objects in the overlapping area. The second object is not drawn except there. If you look closely, you will notice that the resulting color in the overlapping area is different from what the OVER operator produces. Any two operators produce different output in the overlapping area!"
  Data.S "CAIRO_OPERATOR_DEST"
  Data.S "Leaves the first object untouched, the second is discarded completely."
  Data.S "CAIRO_OPERATOR_DEST_OVER"
  Data.S "The result is similar to the OVER operator. Except that the 'order' of the objects is reversed, so the second is drawn below the first."
  Data.S "CAIRO_OPERATOR_DEST_IN"
  Data.S "The blue rectangle is used to determine which part of the red one is left intact. Anything outside the overlapping area is removed. This works like the IN operator, but again with the second object 'below' the first."
  Data.S "CAIRO_OPERATOR_DEST_OUT"
  Data.S "The second object is used to reduce the visibility of the first in the overlapping area. Its transparency/opacity is taken into account. The second object is not drawn itself."
  Data.S "CAIRO_OPERATOR_DEST_ATOP"
  Data.S "Same as the ATOP operator, but again as if the order of the drawing operations had been reversed."
  Data.S "CAIRO_OPERATOR_XOR"
  Data.S "Source and destination are shown where there is only one of them."
  Data.S "CAIRO_OPERATOR_ADD"
  Data.S "Source and destination layers are accumulated."
  Data.S "CAIRO_OPERATOR_SATURATE"
  Data.S "Like OVER, but assuming source and destination layers are disjoint geometries."
  Data.S "CAIRO_OPERATOR_MULTIPLY"
  Data.S "The result color is at least as dark as the darker of the two input colors."
  Data.S "CAIRO_OPERATOR_SCREEN"
  Data.S "Input colors are complemented and multiplied, the product is complemented again. The result is at least as light as the lighter of the input colors."
  Data.S "CAIRO_OPERATOR_OVERLAY"
  Data.S "Multiplies or screens colors, depending on the lightness of the destination color."
  Data.S "CAIRO_OPERATOR_DARKEN"
  Data.S "Selects the darker of the color values in each component."
  Data.S "CAIRO_OPERATOR_LIGHTEN"
  Data.S "Selects the lighter of the color values in each component."
  Data.S "CAIRO_OPERATOR_COLOR_DODGE"
  Data.S "Brightens the destination color by a factor depending on the source color."
  Data.S "CAIRO_OPERATOR_COLOR_BURN"
  Data.S "Darkens the destination color by a factor depending on the source color."
  Data.S "CAIRO_OPERATOR_HARD_LIGHT"
  Data.S "Multiplies or screens colors, depending on the lightness of the source color."
  Data.S "CAIRO_OPERATOR_SOFT_LIGHT"
  Data.S "Darkens or lightens, depending on the source color."
  Data.S "CAIRO_OPERATOR_DIFFERENCE"
  Data.S "Takes the difference of the destination and source colors."
  Data.S "CAIRO_OPERATOR_EXCLUSION"
  Data.S "The effect is similar to DIFFERENCE, but has lower contrast."
  Data.S "CAIRO_OPERATOR_HSL_HUE"
  Data.S "Creates a color with the hue of the source and the saturation and luminosity of the destination."
  Data.S "CAIRO_OPERATOR_HSL_SATURATION"
  Data.S "Creates a color with the saturation of the source and the hue and luminosity of the destination. Painting with this mode onto a gray area produces no change."
  Data.S "CAIRO_OPERATOR_HSL_COLOR"
  Data.S "Creates a color with the hue and saturation of the source and the luminosity of the destination. This preserves the gray levels of the destination and is useful for coloring monochrome images or tinting color images."
  Data.S "CAIRO_OPERATOR_HSL_LUMINOSITY"
  Data.S "Creates a color with the luminosity of the source and the hue and saturation of the destination. This produces an inverse effect to CAIRO_OPERATOR_HSL_COLOR."
EndDataSection

Define CairoVersion.S
Define *EditorOperatorDescription.GtkWidget
Define EventGadget.I
Define FixedBox.I
Define i.I
Define NumOperators.I
Define OperatorIndex.I
Define ProgramID.I
Define *TextOperatorName.GtkWidget
Define *Window.GtkWidget

ProcedureC WidgetExposeHandler(*Widget.GtkWidget, *Event.GdkEventExpose)
  Protected Dim DashInfo.D(1)

  Protected Rectangle.GdkRectangle
  Protected Surface.I
  Protected SurfaceContext.I
  Protected WindowContext.I

  Shared OperatorIndex.I

  ; ----- Clear drawing region for both rectangles
  gdk_window_clear_area_(*Widget\window, 9, 9, 162, 122)

  ; ----- Create Surface for drawing operations
  Surface = cairo_image_surface_create(#CAIRO_FORMAT_ARGB32, 180, 130)
  SurfaceContext = cairo_create(Surface)

  ; ----- Draw left rectangle
  cairo_rectangle(SurfaceContext, 10, 10, 120, 90)
  cairo_set_source_rgba(SurfaceContext, 0.7, 0, 0, 0.8)
  cairo_fill(SurfaceContext)

  ; ----- Set cairo operator for drawing right rectangle
  cairo_set_operator(SurfaceContext, OperatorIndex)

  ; ----- Draw right rectangle
  cairo_rectangle(SurfaceContext, 50, 40, 120, 90)
  cairo_set_source_rgba(SurfaceContext, 0, 0, 0.9, 0.4)
  cairo_fill(SurfaceContext)

  ; ----- Draw image with rectangles into drawing region of GdkWindow
  WindowContext = gdk_cairo_create(*Widget\window)
  cairo_set_operator(SurfaceContext, #CAIRO_OPERATOR_OVER)
  cairo_set_source_surface(WindowContext, Surface, 0, 0)
  cairo_paint(WindowContext)

  ; ----- Draw dotted line around left rectangle
  cairo_set_operator(WindowContext, #CAIRO_OPERATOR_SOURCE)
  cairo_set_source_rgba(WindowContext, 1, 0, 0, 0)
  cairo_set_line_width(WindowContext, 1)
  DashInfo(0) = 1.5
  DashInfo(1) = 1.5
  cairo_set_dash(WindowContext, @DashInfo(), 2, 1)
  cairo_move_to(WindowContext, 10, 10)
  cairo_line_to(WindowContext, 130, 10)
  cairo_line_to(WindowContext, 130, 100)
  cairo_line_to(WindowContext, 10, 100)
  cairo_line_to(WindowContext, 10, 10)
  cairo_stroke(WindowContext)

  ; ----- Draw dotted line around right rectangle
  cairo_move_to(WindowContext, 50, 40)
  cairo_line_to(WindowContext, 170, 40)
  cairo_line_to(WindowContext, 170, 130)
  cairo_line_to(WindowContext, 50, 130)
  cairo_line_to(WindowContext, 50, 40)
  cairo_stroke(WindowContext)

  ; ----- Destroy surface and contexts
  cairo_surface_destroy(Surface)
  cairo_destroy(SurfaceContext)
  cairo_destroy(WindowContext)
EndProcedure

; ----- Obtain Cairo version
ProgramID = RunProgram("pkg-config", "--modversion cairo", "", #PB_Program_Open | #PB_Program_Read)

If ProgramID
  While ProgramRunning(ProgramID)
    CairoVersion + ReadProgramString(ProgramID) + #LF$
  Wend

  CloseProgram(ProgramID)
EndIf

; ----- Cairo has got 15 new operators beginning with version 1.10
If Val(StringField(CairoVersion, 2, ".")) < 10
  NumOperators = 14
Else
  NumOperators = 29
EndIf

Dim OperatorName.S(NumOperators - 1)
Dim OperatorDescription.S(NumOperators - 1)

For i = 0 To NumOperators - 1
  Read.S OperatorName(i)
  Read.S OperatorDescription(i)
Next i

*Window = OpenWindow(0, 270, 100, 610, 180, "Cairo's compositing operators")
FixedBox = g_list_nth_data_(gtk_container_get_children_(gtk_bin_get_child_(*Window)), 0)
g_signal_connect_(FixedBox, "expose-event", @WidgetExposeHandler(), 0)
WidgetExposeHandler(FixedBox, 0)

If LoadFont(0, "Arial", 11)
  SetGadgetFont(#PB_Default, FontID(0))
EndIf

*TextOperatorName = TextGadget(#TextOperatorName, 200, 10, 400, 20, "")
SetGadgetText(#TextOperatorName, "#" + OperatorName(OperatorIndex) + " = " + Str(OperatorIndex))

*EditorOperatorDescription = EditorGadget(#EditorOperatorDescription, 200, 40, 400, 93, #PB_Editor_ReadOnly)
SetGadgetText(#EditorOperatorDescription, OperatorDescription(OperatorIndex))

; ----- Disable frame around EditorGadget
; gtk_text_view_set_border_window_size_(*EditorOperatorDescription, #GTK_TEXT_WINDOW_LEFT, 0)

; ----- Change background color of EditorGadget to background color of TextGadget
gtk_widget_modify_base_(*EditorOperatorDescription, #GTK_STATE_NORMAL, *TextOperatorName\style\bg)

; ----- Enable word wrap in EditorGadget
gtk_text_view_set_wrap_mode_(GadgetID(#EditorOperatorDescription), #GTK_WRAP_WORD)

ButtonGadget(#ButtonPrevious, 150, WindowHeight(0) - 35, 150, 25, "< Previous Operator")
DisableGadget(#ButtonPrevious, #True) 
ButtonGadget(#ButtonNext, 320, WindowHeight(0) - 35, 150, 25, "Next Operator >")

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      EventGadget = EventGadget()

      If EventGadget = #ButtonPrevious Or EventGadget = #ButtonNext
        If EventType() = #PB_EventType_LeftClick
          If EventGadget = #ButtonPrevious
            OperatorIndex - 1
            
            If OperatorIndex = 0
              DisableGadget(#ButtonPrevious, #True)
            EndIf
          Else
            OperatorIndex + 1
            
            If OperatorIndex = NumOperators - 1
              DisableGadget(#ButtonNext, #True) 
            EndIf
          EndIf

          If OperatorIndex > 0
            DisableGadget(#ButtonPrevious, #False)
          EndIf

          If OperatorIndex < NumOperators - 1
            DisableGadget(#ButtonNext, #False)
          EndIf

          SetGadgetText(#TextOperatorName, "#" + OperatorName(OperatorIndex) + " = " + Str(OperatorIndex))
          SetGadgetText(#EditorOperatorDescription, OperatorDescription(OperatorIndex))
          WidgetExposeHandler(FixedBox, 0)
        EndIf
      EndIf
  EndSelect
ForEver
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Demo of Cairo's compositing operators

Post by idle »

thanks nice demo
Windows 11, Manjaro, Raspberry Pi OS
Image
Post Reply