Colorize an image

Share your advanced PureBasic knowledge/code with the community.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Colorize an image

Post by wilbert »

BasicallyPure wrote:By choosing the 'palette' option your module is used.
By choosing the 'direct' option my procedure is used.
The results look the same to me.
Am I using your module wrong or do you agree that they both give the same results?
The multiply method is the same as what you are doing so yes, in this case they look almost similar.

The other two methods are different.
The overlay method does produce a duotone (see https://en.wikipedia.org/wiki/Duotone) and works best when you choose a 'mid' color (not very dark and not very bright) like
Colorize::ColorizeImage(0, $4070a0, Colorize::#Overlay)
BasicallyPure wrote:I found these images on the internet. This is what I think duotone should look like.
Many other images I saw that claim to be duotone look like monotone to me. :shock:
If you think of a white paper, with one ink color for the dark tones and one ink color for the mid tones, that's what I would call duotone.
The overlay method I presented does this but with the color for the dark tones fixed to black (it only allows you to specify the color for the mid tones).
The image you posted on the right sets both the colors for the dark and mid tones although I have no idea at the moment how to create this.
The effect for the left image (the staring man) is much easier to accomplish.
Last edited by wilbert on Sat Feb 06, 2016 5:01 pm, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Colorize an image

Post by BasicallyPure »

wilbert wrote:The overlay method does produce a duotone (see https://en.wikipedia.org/wiki/Duotone)
OK, that works. I was able to take the image from the Wikipedia link you gave and convert it to grayscale.
Then using your module with the #Overlay option, I could very closely restore it to the original appearance.

As I work with these image manipulation techniques I often struggle with understanding the exact meaning of many of the terms.
Some examples are Hue, tint, shade, and tone.
I like to understand them in terms of RGB color space but sometimes maybe it is better to try and understand other types of
color space such as HSV.

Anyway, when looking at the Wikipedia entry for Hue notice the nice graphic on the right side.
It goes on to say "hue refers to a pure color—one without tint or shade".
So to understand that you have to also have an understanding of tint and shade.
I find it hard to relate what that means exactly in RGB color space.
Fortunately I have been able to duplicate the nice hue graphic with some code.

This is how it works.
As you travel across the x-axis through the various hues there a couple of rules that seem to hold true.
1. One of the RGB color values is always held at 255.
2. One of the RGB color values is always held at 0.
3. The remaining RGB value is varied from 0 to 255

With this in mind my current understanding is that these are the only 'pure colors' without tint or shade.
If the RGB value that is always 0 is changed to something other than 0 then 'tint' has been added to the hue.
If the RGB value that is always 255 is changed to something other than 255 then 'shade' has been added.

Until I learn better, those are the definitions that I am going with.
I still have to understand saturation although I think I have some understanding what it means.
So here is the code that appears to duplicate the hue graphic.
It looks like there are 1536 possible pure hues.

Code: Select all

EnableExplicit

Macro MakeHueGradient(m, n)
   While m < $100
      LineXY(x,0,x,255,RGB(R,G,B))
      x + 1
      m + 1
   Wend
   m - 1
   
   While n > -1
      LineXY(x,0,x,255,RGB(R,G,B))
      x + 1
      n - 1
   Wend
   n + 1
EndMacro

If OpenWindow(0,0,0,1536,256,"Hue",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
   Define R, G, B, x, c
   Define img = CreateImage(#PB_Any,1536,256)
   
   R = $FF : G = 0 : B = 0 : x = 0 ;starting conditions
   StartDrawing(ImageOutput(img))
      MakeHueGradient(G, R) ; green inc from 0 to 255, red dec from 255 to 0.
      MakeHueGradient(B, G) ; blue inc from 0 to 255, green dec from 255 t 0.
      MakeHueGradient(R, B) ; red inc from 0 to 255, blue dec from 255 to 0.
   StopDrawing()
   
   Debug x ; 256 * 6 = 1536
   
   ImageGadget(#PB_Any,0,0,1536,256,ImageID(img))
   
   While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend
   
EndIf
Last edited by BasicallyPure on Thu Jul 28, 2016 10:13 pm, edited 1 time in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Colorize an image

Post by wilbert »

BasicallyPure wrote:OK, that works. I was able to take the image from the Wikipedia link you gave and convert it to grayscale.
Then using your module with the #Overlay option, I could very closely restore it to the original appearance.
I'll try if I can create something that can do the effect of the images you showed as well.
BasicallyPure wrote:I like to understand them in terms of RGB color space but sometimes maybe it is better to try and understand other types of
color space such as HSV.
Some things can only be done in HSV color space I believe but RGB is easier to work with.
This page contains also a lot of information
https://en.wikipedia.org/wiki/HSL_and_HSV
BasicallyPure wrote:Fortunately I have been able to duplicate the nice hue graphic with some code.
That looks nice :)
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Colorize an image

Post by wilbert »

I added a ColorizeImageSMH procedure to my module.
It allows you to create something similar to the two images you posted before.

Colorize::ColorizeImageSMH(0, $800000, $907040, $a0e080); blue/green
Colorize::ColorizeImageSMH(0, $800000, $ff80ff, $ffffff); blue/magenta/white


It's a bit slower compared to the other procedures I created.
Converting to 1024 gray tones first combined with a simple lookup table would be easier and faster but that would make it more difficult to handle channel swapping required on Windows when working directly with the drawing buffer.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Colorize an image

Post by BasicallyPure »

Thank you Wilbert for the new module feature.
I will be experimenting with it soon.

After some more experiments with my DuoTone() procedure that I posted before
I've decided I was premature in dismissing it as a useful method.
After tweaking the threshold level and choosing the two colors more carefully plus
adding dither I think the result is rather good.
It is also easy to see how I could add more tones to have the option of 3 or 4 and
not be limited to just two. The bigger challenge is going to be creating an efficient
user interface that allows for easy selection of colors and threshold points.
Choosing a good source image is also important I think as the intensity variation
throughout the image (contrast between subject and background) will have a big
impact on the result.

I was able to produce this with a modified version of my DuoTone procedure.
Image
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Colorize an image

Post by wilbert »

BasicallyPure wrote:I was able to produce this with a modified version of my DuoTone procedure.
That looks nice :)

And you are right about the source image.
It makes a lot of difference for the result if there's a lot of intensity variation.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Colorize an image

Post by BasicallyPure »

wilbert wrote:That looks nice
To be fair, Katie Holmes always 'looks nice' :)
Perhaps I should have chosen a different image, maybe a frog (no offense to netMaestro).
That would have allowed us to be more objective and unbiased in our judgment.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Colorize an image

Post by wilbert »

Here are a few tests with my ColorizeImageSMH procedure.

Image

Image

Image

Image

If you or someone else wants to understand the procedure, here it is in PureBasic only code.
The drawback is that it's a lot slower compared to the asm procedure inside my module.

I chose to change the coefficients used for luma conversion to 0.2126, 0.7152, and 0.0722 (BT.709) .
To me the results these constants give looks a little better.

Code: Select all

Procedure ColorizeImageSMH(Image, Shadow = $000000, Midtone = $808080, Highlight = $ffffff)
  
  Protected.i c, x, y, w, h
  Protected.u l0,l1, r0,g0,b0, r1,g1,b1, r2,g2,b2
  
  ; expand color components to 16 bit unsigned
  r0 = Red  (Shadow) * 257
  g0 = Green(Shadow) * 257
  b0 = Blue (Shadow) * 257
  r1 = Red  (Midtone) * 257
  g1 = Green(Midtone) * 257
  b1 = Blue (Midtone) * 257
  r2 = Red  (Highlight) * 257
  g2 = Green(Highlight) * 257
  b2 = Blue (Highlight) * 257
  
  ; process image
  If IsImage(Image) And StartDrawing(ImageOutput(Image))
    DrawingMode(#PB_2DDrawing_AllChannels)
    w = OutputWidth()
    h = OutputHeight()
    While y < h
      x = 0
      While x < w
        c = Point(x, y)
        ; calculate luma
        l0 = ((c & $ff)*$36a361 + (c >> 8 & $ff)*$b7ce70 + (c >> 16 & $ff)*$128e2f) >> 16
        If l0 & $8000
          ; midtone to highlight range
          l0 << 1
          l1 = ~l0
          Plot(x, y, ((l0*r2+l1*r1)>>24 & $ff)|((l0*g2+l1*g1)>>16 & $ff00)|((l0*b2+l1*b1)>>8 & $ff0000)|(c & $ff000000))
        Else
          ; shadow to midtone range
          l0 << 1
          l1 = ~l0
          Plot(x, y, ((l0*r1+l1*r0)>>24 & $ff)|((l0*g1+l1*g0)>>16 & $ff00)|((l0*b1+l1*b0)>>8 & $ff0000)|(c & $ff000000))
        EndIf
        x + 1
      Wend
      y + 1
    Wend
    StopDrawing()
  EndIf
  
EndProcedure
Last edited by wilbert on Wed May 31, 2017 3:07 pm, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Colorize an image

Post by BasicallyPure »

Good job Wilbert and thanks for the PureBasic version.

I have been hard at work on my new user interface.
It's mostly finished I think but I have not integrated it into
the main program yet.
Here is what it looks like.
Image
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Colorize an image

Post by wilbert »

Great work on the interface.
Would be nice to see it integrated with your CQ Image Editor. :)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Colorize an image

Post by BasicallyPure »

I didn't forget, it just took longer than I would have liked.

Images can now be colorized with one to four tones when using the direct method.

The threshold spin gadgets can be used to adjust the transition points between tones.
Example, when using duo tones the default transition is set for a value of 128.
For all luminosity values above 128 color tone one will be used and below 128,
color tone two will be used.

Colors can be selected using the available controls or by clicking directly on the source
image. The color under the cursor will be captured.
It often works best to use a color of high luminosity. For convenience a color can be
automatically adjusted to maximum luminosity by right clicking on the gradient or
palette display. When the color is to your liking click on one of the set buttons C1-C4.

There are preset colors available that often work well with the direct method.
When using the palette method better results are often obtained by manually choosing
dominant colors from the source image.

Here are a couple of examples that give an idea of what can be done.
Image

Image
The palette method is actually a way to quantize the colors of the image and not a 'colorize' toning process.
It works very well if you choose the colors carefully, I chose only four colors from the source image and used
the 'quad' setting to yield a palette of only 16 colors even though the resulting image appears to have many more
than 16 colors.

[edit] 2/18/2016, Fixed load preset colors problem for Linux users.

[edit] 3/2/2016, added new features 'blend method' based on Wilbert's code above.
now images can be colorized using duotone, tritone, and quadtone methods.

[edit] 9/19/2016, Previous threshold settings are now retained in the direct method after switching back
from other methods.

Code: Select all

; ColorizeImage.pb
; apply specific color(s) to image by BasicallyPure
; 9.19,2016
; License : Free
; Compiler: PureBasic 5.50 LTS
; OS: cross platform

EnableExplicit

UsePNGImageDecoder()      : UsePNGImageEncoder()
UseJPEGImageDecoder()     : UseJPEGImageEncoder()
UseJPEG2000ImageDecoder() : UseJPEG2000ImageEncoder()

Structure PresetType
   na.s ; preset name
   C1.i ; tone 1 value
   C2.i
   C3.i
   C4.i
EndStructure

;{ procedure declarations
Declare ASSEMBLE_TO_PALETTE(image, Array Pallete.l(1))
Declare CAPTURE_COLOR_POINT()
Declare CATCH_PALETTE(*MemoryAddress.Long, NumColors.i)
Declare COLORIZE_CANVAS_CALLBACK()
Declare COLORIZE_IMAGE(image.i)
Declare COLORIZE_QUADTONE(Image.i)
Declare COPY()
Declare CREATE_PALETTE(size.i)
Declare DRAW_PALETTE(style.i)
Declare DRAW_GRAD(image.i, shift.i)
Declare EVENT_LOOP()
Declare.l FIND_NEAREST(Color.l)
Declare FIT_CANVAS_TO_WINDOW()
Declare FIT_WINDOW_TO_IMAGE(image.i)
Declare HANDLE_WIN_COLORIZE_EVENTS(event.i)
Declare HANDLE_WIN_MAIN_EVENTS(event.i)
Declare LOAD_IMAGE()
Declare LOAD_PRESET()
Declare OPEN_COLORIZE_WINDOW()
Declare OPEN_MAIN_WINDOW()
Declare PASTE()
Declare.l PointOrdDith(x.i, y.i)
Declare REDRAW_CUSTOM_TRACKBAR(canvas.i)
Declare REVERT()
Declare SAVE_IMAGE()
Declare SPIN_ADJUST(gadget)
Declare T_COLOR_UPDATE()
Declare UPDATE_CANVAS(image.i)
Declare UPDATE_METHOD(gadget.i)
Declare UPDATE_TONEOPTION(gadget.i) : ;}

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
   Macro rdx : edx : EndMacro
   #x64 = #False
CompilerElse
   #x64 = #True
CompilerEndIf

#WinMain     = 0
#WinColorize = 1
#WinPreset   = 2

#MainMenu = 0
#PopMenu  = 1

#CBcolor  = $404040 ;canvas background color

;{ enumerations
Enumeration ;images
   #imgRef    : #imgRevert : #imgAdj
   #img_Adj_R : #img_Adj_G : #img_Adj_B : #img_C1 : #img_C2
   #img_C3    : #img_C4    : #img_Hue  : #img_Tone
EndEnumeration

Enumeration
   #Canvas ; for main window
   ; gadgets for colorize window
   #btn_C1_set : #btn_C2_set : #btn_C3_set : #btn_C4_set : #btn_preview : #btn_apply : #btn_close : #Btn_source
   #Fra_Hue : #Fra_Grad : #Fra_C1 : #Fra_C2 : #Fra_Method : #Fra_C3 : #Fra_C4 : #Fra_palSize : #Fra_Modify
   #opt_tri : #opt_mono : #opt_duo : #opt_quad : #opt_direct : #opt_palette : #opt_blend
   #imgGad_C1 : #imgGad_C2 : #imgGad_C3 : #imgGad_C4 : #ImgGad_Tone
   #spin_thresh_1 : #spin_thresh_2 : #spin_thresh_3
   #Can_Hue : #Can_Adj_R : #Can_Adj_G : #Can_Adj_B
   #Combo_presets : #chk_dither : #trk_palSize
EndEnumeration

Enumeration ;menu items
   #MenuItem_Load  : #MenuItem_Exit : #MenuItem_Colorize : #MenuItem_Copy
   #MenuItem_Paste : #MenuItem_Save : #MenuItem_Revert   : #MenuItem_Enter
EndEnumeration : ;}

;{ global variables
Global NewMap PresetMap.i()
Global NewList presetList.PresetType()
   AddElement(presetList())
   Restore PresetData
   Read.s presetList()\na
   While presetList()\na <> "End_Data"
      Read.i presetList()\C1
      Read.i presetList()\C2
      Read.i presetList()\C3
      Read.i presetList()\C4
      AddElement(presetList())
      Read.s presetList()\na
   Wend
   DeleteElement(presetList())
   
Global Dim Hue_Palette.l(0)
Global Dim IndexG.l(255)
Global Dim Palette.l(1)
Global Dim hue.i(1535)
Global Dim tone(3)
Global CurrentFileName$, File$
Global Title$ = "Colorize image "
Global endProgram = #False
Global DW,DH,LinuxAdj
Global CanvasMaxWidth, CanvasMaxHeight
Global CanvasMinWidth, CanvasMinHeight
Global WinBorder_X, WinBorder_Y
Global Pan_X, Pan_Y, oversize
Global ColorizeWinActive
Global HueSet.i = 264
Global paletteSize = 6
Global toneOption = #opt_quad
Global SM1 = 160, SM2 = 96, SM3 = 64
Global pMlt = 4 ;quad
Global method = #opt_blend
Global Tcolor = $FFFF00
Global preset = 4
Global DragPrivateImage
Global dither = #False : ;}

Macro MakeHueGradient(m, n)
   ;used in OPEN_COLORIZE_WINDOW() procedure
   While m < $100
      Hue(x) = RGB(R,G,B)
      LineXY(x,0,x,20,Hue(x))
      x + 1
      m + 1
   Wend
   m - 1
   
   While n > -1
      Hue(x) = RGB(R,G,B)
      LineXY(x,0,x,20,Hue(x))
      x + 1
      n - 1
   Wend
   n + 1
EndMacro

If OPEN_MAIN_WINDOW()
   EVENT_LOOP()
EndIf

End

Procedure ASSEMBLE_TO_PALETTE(image.i, Array palette.l(1))
   ;examine each pixel of an image and assign the nearest color
   ;from the given palette.
   
   Protected.i h, w, x, y, dither = GetGadgetState(#Chk_Dither)
   
   CATCH_PALETTE(@palette(), ArraySize(palette())+1)
   
   If StartDrawing(ImageOutput(Image))
         h = OutputHeight()
         w = OutputWidth()
         If dither
            While y < h
               x = 0
               While x < w
                  Plot(x, y, FIND_NEAREST(PointOrdDith(x, y)))
                  x + 1
               Wend
               y + 1
            Wend
         Else
            While y < h
               x = 0
               While x < w
                  Plot(x, y, FIND_NEAREST(Point(x, y)))
                  x + 1
               Wend
               y + 1
            Wend
         EndIf
      StopDrawing()
   EndIf
   
EndProcedure
Procedure CANVAS_CALLBACK()
   ;respond to events from the main window canvas gadget
   
   Static drag, Xorg, Yorg, iw, ih, ow, oh, Llim, Rlim, Tlim, Blim
   Protected mx, my, x, y
   Select EventType()
      Case #PB_EventType_RightButtonUp : PostEvent(#PB_Event_RightClick)
      ; triggers a '#PB_Event_RightClick' for pop-up menu activation
      ; just like right clicking on the main window.
      Case #PB_EventType_LeftButtonDown
         If oversize : drag = #True
            Xorg = GetGadgetAttribute(#Canvas,#PB_Canvas_MouseX)
            Yorg = GetGadgetAttribute(#Canvas,#PB_Canvas_MouseY)
               ow = GadgetWidth(#Canvas)  : iw = ImageWidth(#imgRef)
               oh = GadgetHeight(#Canvas) : ih = ImageHeight(#imgRef)
               
               Llim = ow-iw : If Llim > 0 : Llim = 0 : EndIf
               Rlim = ow-iw : If Rlim < 0 : Rlim = 0 : EndIf
               
               Tlim = oh-ih : If Tlim > 0 : Tlim = 0 : EndIf
               Blim = oh-ih : If Blim < 0 : Blim = 0 : EndIf
         EndIf
      Case #PB_EventType_MouseMove
         If drag
            mx = GetGadgetAttribute(#Canvas,#PB_Canvas_MouseX)
            my = GetGadgetAttribute(#Canvas,#PB_Canvas_MouseY)
           
            If iw > ow : Pan_X + (mx - Xorg) : Xorg = mx : EndIf
            If ih > oh : Pan_Y + (my - Yorg) : Yorg = my : EndIf
           
            ; pan canvas image
            StartDrawing(CanvasOutput(#Canvas))
               Box(0,0,ow,oh,#CBcolor)
               
               x = Pan_X + (ow - iw) / 2
               y = Pan_Y + (oh - Ih) / 2
               
               If x < Llim : Pan_X + (Llim - x) : x = Llim : EndIf
               If x > Rlim : Pan_X - (x - Rlim) : x = Rlim : EndIf
               If y < Tlim : Pan_Y + (Tlim - y) : y = Tlim : EndIf
               If y > Blim : Pan_Y - (y - Blim) : y = Blim : EndIf
               
               DrawImage(ImageID(#imgRef),x,y)
            StopDrawing()
         EndIf
         
      Case #PB_EventType_LeftButtonUp
         drag = #False
         If ColorizeWinActive
            CAPTURE_COLOR_POINT()
         EndIf
   EndSelect
EndProcedure
Procedure CATCH_PALETTE(*MemoryAddress.Long, NumColors.i)
   ; Catch a palette from memory
   Protected.i i, j = 1
   ReDim Palette(NumColors + 1)
   Palette(0) = 0 : Palette(NumColors + 1) = 0
   For i = 1 To NumColors
      Palette(i) = $ff000000 | *MemoryAddress\l
      *MemoryAddress + 4
   Next
   SortStructuredArray(Palette(), 0, 0, #PB_Unicode, 1, NumColors)
   For i = 0 To 255
      IndexG(i) = j
      While ((Palette(j) >> 8) & $ff) = i And j < NumColors
         j + 1
      Wend
      IndexG(i) = (IndexG(i) + j) >> 1
   Next
EndProcedure
Procedure CAPTURE_COLOR_POINT()
   Protected mx, my
   
   StartDrawing(CanvasOutput(#Canvas))
      mx = GetGadgetAttribute(#Canvas,#PB_Canvas_MouseX)
      my = GetGadgetAttribute(#Canvas,#PB_Canvas_MouseY)
      Tcolor = Point(mx,my)
   StopDrawing()
   
   SetGadgetData(#Can_Adj_R,Red(Tcolor))   : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_R)
   SetGadgetData(#Can_Adj_G,Green(Tcolor)) : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_G)
   SetGadgetData(#Can_Adj_B,Blue(Tcolor))  : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_B)
   
   DRAW_PALETTE(#opt_direct)
   SetGadgetText(#Fra_Modify,"Modify | " + RSet(Hex(Tcolor),6,"0"))
EndProcedure
Procedure COLORIZE_CANVAS_CALLBACK()
   ;respond to events from any of the canvas gadgets
   ;on the colorize control window.  These are the
   ;custom trackbar gadgets.
   
   Static c, cxor, drag, mx, my, img, gadget, scale.d
   
   gadget = EventGadget()
   Select gadget
      Case #Can_Hue   : img = #img_Hue   : cxor = $FFFFFF : scale = 1535 / 529
      Case #Can_Adj_R : img = #img_Adj_R : cxor = $00FFFF : scale = 1
      Case #Can_Adj_G : img = #img_Adj_G : cxor = $FF00FF : scale = 1
      Case #Can_Adj_B : img = #img_Adj_B : cxor = $00FFFF : scale = 1
      Default : ProcedureReturn
   EndSelect
   
   Select EventType()
      Case #PB_EventType_LeftButtonDown : drag = #True
         mx = GetGadgetAttribute(gadget,#PB_Canvas_MouseX)
         StartDrawing(CanvasOutput(gadget))
            DrawImage(ImageID(img),0,0)
            DrawingMode(#PB_2DDrawing_XOr)
            Box(mx-1,0,3,20,cxor)
         StopDrawing()
         Select gadget
            Case #Can_Hue : SetGadgetData(gadget, hue(Int(mx * scale)))
               SetGadgetText(#Fra_Hue,"HUE | " + RSet(Hex(hue(Int(mx * scale))),6,"0"))
            Default       : SetGadgetData(gadget, mx * scale)
         EndSelect
      Case #PB_EventType_MouseMove
         If drag
            mx = GetGadgetAttribute(gadget,#PB_Canvas_MouseX)
            StartDrawing(CanvasOutput(gadget))
               DrawImage(ImageID(img),0,0)
               DrawingMode(#PB_2DDrawing_XOr)
               Box(mx-1,0,3,20,cxor)
            StopDrawing()
            Select gadget
               Case #Can_Hue : SetGadgetData(gadget, hue(Int(mx * scale)))
                  SetGadgetText(#Fra_Hue,"HUE | " + RSet(Hex(hue(Int(mx * scale))),6,"0"))
               Default       : SetGadgetData(gadget, mx * scale)
            EndSelect
         EndIf
      Case #PB_EventType_LeftButtonUp, #PB_EventType_MouseLeave
         If drag : drag = #False
            If gadget = #Can_Hue
               c = hue(Int(mx * scale))
               SetGadgetData(#Can_Adj_R,Red(c))   : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_R)
               SetGadgetData(#Can_Adj_G,Green(c)) : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_G)
               SetGadgetData(#Can_Adj_B,Blue(c))  : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_B)
               Tcolor = c
            Else
               Tcolor = RGB(GetGadgetData(#Can_Adj_R),GetGadgetData(#Can_Adj_G),
                                          GetGadgetData(#Can_Adj_B))
            EndIf
           
            SetGadgetText(#Fra_Modify,"Modify | " + RSet(Hex(Tcolor),6,"0"))
            DRAW_PALETTE(#opt_direct)
         EndIf
         
   EndSelect
EndProcedure
Procedure COLORIZE_IMAGE(image.i)
   ;direct method, no blending between color transitions
   ;global array tone() holds 4 blending colors
   ;option gadgets determine how many of the colors are used
   
   Protected.i c, i, x, y, xMax, yMax
   Protected.d r1, g1, b1, r2, g2, b2, r3, g3, b3, r4, g4, b4
   Protected.i dither = GetGadgetState(#Chk_Dither)
   Protected.i th1 = GetGadgetState(#spin_thresh_1) * 7
   Protected.i th2 = GetGadgetState(#spin_thresh_2) * 7
   Protected.i th3 = GetGadgetState(#spin_thresh_3) * 7
   
   r1 = Red(tone(0))  /1785 : r2 = Red(tone(1))  /1785 : r3 = Red(tone(2))  /1785 : r4 = Red(tone(3))  /1785
   g1 = Green(tone(0))/1785 : g2 = Green(tone(1))/1785 : g3 = Green(tone(2))/1785 : g4 = Green(tone(3))/1785
   b1 = Blue(tone(0)) /1785 : b2 = Blue(tone(1)) /1785 : b3 = Blue(tone(2)) /1785 : b4 = Blue(tone(3)) /1785
   
   If IsImage(image)
     
      StartDrawing(ImageOutput(image))
         xMax = OutputWidth()  - 1
         yMax = OutputHeight() - 1
         
         For y = 0 To yMax
            For x = 0 To xMax
               If dither
                  c = PointOrdDith(x, y)
               Else
                  c = Point(x, y)
               EndIf
               
               i = (c & $FF) << 1 : c >> 8
               i + (C & $FF) << 2 : c >> 8
               i + (C & $FF)
               
               If i > th1
                  Plot(x, y, RGB(r1*i, g1*i, b1*i))
               ElseIf i > th2
                  Plot(x, y, RGB(r2*i, g2*i, b2*i))
               ElseIf i > th3
                  Plot(x, y, RGB(r3*i, g3*i, b3*i))
               Else
                  Plot(x, y, RGB(r4*i, g4*i, b4*i))
               EndIf

            Next x
         Next y
      StopDrawing()
     
   EndIf
EndProcedure
Procedure COLORIZE_QUADTONE(Image)
   ;colorize image with smooth transitions between colors
   ;global array tone() holds 4 blending colors
   ;option gadgets determine how many of the colors are used
   ;blending with 2, 3, or 4 colors is possible
   
   Protected.i c, x, y, w, h
   Protected.i R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4
   Protected.i lum_1, lum_2, DkR, DkG, DkB, tc
   
   ; determine how many colors to blend, default = 2
   If GetGadgetState(#opt_tri)
      tc = %01
   ElseIf GetGadgetState(#opt_quad)
      tc = %11
   EndIf
   
   ; expand color components
   R1 = Red(tone(0)) : G1 = Green(tone(0)) : B1 = Blue(tone(0))
   R2 = Red(tone(1)) : G2 = Green(tone(1)) : B2 = Blue(tone(1))
   R3 = Red(tone(2)) : G3 = Green(tone(2)) : B3 = Blue(tone(2))
   R4 = Red(tone(3)) : G4 = Green(tone(3)) : B4 = Blue(tone(3))
   
   Select tc ; determinw which color will blend to dark
      Case %00 : DkR = R2 : DkG = G2 : DkB = B2 ;Duotone
      Case %01 : DkR = R3 : DkG = G3 : DkB = B3 ;TriTone
      Case %11 : DkR = R4 : DkG = G4 : DkB = B4 ;QuadTone
   EndSelect
   
   ; process image
   If IsImage(Image) And StartDrawing(ImageOutput(Image))
         w = OutputWidth()
         h = OutputHeight()
         While y < h
            x = 0
            While x < w
               c = Point(x, y)
               
               ; calculate luma where lum_1 <= $1FFFF
               ; weight = R*0.299, G*0.587, B*0.114
               lum_1 = c & $FF * $266C0 : c >> 8 ; red
               lum_1 + c & $FF * $4B6E2 : c >> 8 ; grn
               lum_1 + c & $FF * $0EA63          ; blu
               lum_1 >> 10
               
               If lum_1 > $FFFF ; C1 to C2 range
                  lum_1 & $FFFF
                  lum_2 = ($FFFF - lum_1)>>1
                  Plot(x, y,
                       RGB((lum_1*R1 + lum_2*R2)>>16, ;red
                           (lum_1*G1 + lum_2*G2)>>16, ;grn
                           (lum_1*B1 + lum_2*B2)>>16));blu
                  
               ElseIf (tc & %01) And lum_1 > $7FFF ; C2 to C3 range
                  lum_1 & $7FFF
                  lum_2 = ($7FFF - lum_1)>>1
                  Plot(x, y,
                       RGB((lum_1*R2 + lum_2*R3)>>16, ;red
                           (lum_1*G2 + lum_2*G3)>>16, ;grn
                           (lum_1*B2 + lum_2*B3)>>16));blu
                  
               ElseIf (tc & %10) And lum_1 > $3FFF ; C3 to C4 range
                  lum_1 & $3FFF
                  lum_2 = ($3FFF - lum_1)>>1
                  Plot(x, y,
                       RGB((lum_1*R3 + lum_2*R4)>>16, ;red
                           (lum_1*G3 + lum_2*G4)>>16, ;grn
                           (lum_1*B3 + lum_2*B4)>>16));blu
               
               Else ; C? to dark range
                  lum_1 >> 1
                  Plot(x, y,
                       RGB((lum_1*DkR)>>16, ;red
                           (lum_1*DkG)>>16, ;grn
                           (lum_1*DkB)>>16));blu
               EndIf
               
               x + 1
            Wend
            y + 1
         Wend
         
      StopDrawing()
   EndIf
   
EndProcedure
Procedure COPY()
   ; copy active image to clipboard
   Protected timeout, img, msgWin, x, y, t$ = "copied to clipboard."
   
   If ColorizeWinActive
      SetClipboardImage(#imgAdj)
   Else
      SetClipboardImage(#imgRef)
   EndIf
   
   x = ImageWidth(#imgRef)/2 + 30 + WindowX(#WinMain)
   y = ImageHeight(#imgRef)/2 - 15 + WindowY(#WinMain)
   
   msgWin = OpenWindow(#PB_Any,x,y,200,30,"",#PB_Window_BorderLess)
   TextGadget(#PB_Any,0,5,200,25,t$,#PB_Text_Center)
   timeout = ElapsedMilliseconds() + 1250
   
   While ElapsedMilliseconds() < timeout
      While WindowEvent() : Wend
      Delay(10)
   Wend
   
   CloseWindow(msgWin)
EndProcedure
Procedure CREATE_PALETTE(size.i)
   Protected.f inc, total, Rr, Gr, Br, Mr
   Protected.i count, max, sum, idx, offset, r, g, b, n
   
   Select toneOption
      Case #opt_mono : ReDim Hue_Palette(size*1 -1) : n = 0
      Case #opt_duo  : ReDim Hue_Palette(size*2 -1) : n = 1
      Case #opt_tri  : ReDim Hue_Palette(size*3 -1) : n = 2
      Case #opt_quad : ReDim Hue_Palette(size*4 -1) : n = 3
   EndSelect
   
   For count = 0 To n
      r = Red(tone(count))
      g = Green(tone(count))
      b = Blue(tone(count))
     
      sum = r + g + b
      max = g
      If r > max : max = r : EndIf
      If b > max : max = b : EndIf
     
      inc = max  / (size)
      Rr = r / sum
      Gr = g / sum
      Br = b / sum
      Mr = max / sum
     
      offset = count * size
      For idx = 0 To size - 1
         total = (max - inc * idx) / Mr ; calc' R+G+B total
         r = Round(total * Rr, #PB_Round_Nearest)
         g = Round(total * Gr, #PB_Round_Nearest)
         b = Round(total * Br, #PB_Round_Nearest)
         Hue_Palette(idx + offset) = (r + g<<8 + b<<16)
      Next idx
   Next count
EndProcedure
Procedure DRAW_PALETTE(style.i)
   Protected.i xMax, yMax, i, j, n, size = GetGadgetState(#trk_palSize)
   Protected.f x, r, g, b, h, inc, yi, y1, y2
   
   StartDrawing(ImageOutput(#img_Tone))
      xMax = OutputWidth()  - 1
      yMax = OutputHeight() - 1
      inc = OutputWidth() / 256
      h = OutputHeight() - 1
     
      If style = #opt_palette
         CREATE_PALETTE(size)
         
         Select toneOption
            Case #opt_mono : n = 1
            Case #opt_duo  : n = 2
            Case #opt_tri  : n = 3
            Case #opt_quad : n = 4
         EndSelect
         
         pMlt = n
         
         inc = Round(OutputWidth() / size, #PB_Round_Nearest)
         yi = OutputHeight() / n
         y1 = 0
         y2 = yi
         
         For j = 1 To n
            x = xMax
            For i = 0 To size - 1
               Box(x-inc, y1, inc, y2, Hue_Palette(i + Int(size*(j-1))))
               x - inc
            Next i
            y1 + yi
            y2 + yi
         Next j
      Else
         r = Red(Tcolor)
         g = Green(Tcolor)
         b = Blue(Tcolor)
         
         r/xMax : g/xMax : b/xMax
         x = 0
         
         Repeat
            LineXY(x, 0, x, yMax, RGB(r*x, g*x, b*x))
            x + inc
         Until x > xMax : x - inc
         
      EndIf
   StopDrawing()
   
   SetGadgetState(#ImgGad_Tone,ImageID(#img_Tone))
   SetGadgetText(#Fra_palSize,Str(paletteSize * pMlt))
EndProcedure
Procedure DRAW_GRAD(image.i, shift)
   Protected inc.f, c.f, x.i
   
   StartDrawing(ImageOutput(image))
      inc = (256 / OutputWidth())
      c = 0
      For x = 0 To OutputWidth() - 1
         LineXY(x,0,x,19,Int(c)<<shift)
         c + inc
      Next
   StopDrawing()
   
EndProcedure
Procedure EVENT_LOOP()
   Protected event
   
   Repeat
      event = WaitWindowEvent()
      Select EventWindow()
         Case #WinMain     : HANDLE_WIN_MAIN_EVENTS(event)
         Case #WinColorize : HANDLE_WIN_COLORIZE_EVENTS(event)
      EndSelect
   Until endProgram = #True
   
EndProcedure
Procedure.l FIND_NEAREST(Color.l)
   ; Find the nearest color
   ; Taken from NearestColor module by Wilbert
   ; Latest updated : Jan 27, 2016
   ; Color distance formula based on:
   ; http://www.compuphase.com/cmetric.htm

   Protected.l c, c0, c1, d, bestd = $12000000
   Protected.Long *p0, *p1
   
   EnableASM
   
   Macro M_FindNearest(i, st)
      !nearestcolor.findnearest#i#_loop:
      !mov ecx, [p.v_c#i#]
      !test ecx, ecx
      !jz nearestcolor.findnearest#i#_cont2
      !movzx eax, byte [p.v_Color + 1]
      !movzx ecx, ch
      !sub eax, ecx
      !imul eax, eax
      !shl eax, 11
      !cmp eax, [p.v_bestd]
      !jnc nearestcolor.findnearest#i#_cont1
      !mov [p.v_d], eax
      !movzx eax, byte [p.v_Color]
      !movzx ecx, byte [p.v_c#i#]
      !lea edx, [eax + ecx]   ; edx = rsum
      !sub eax, ecx
      !imul eax, eax          ; eax = r*r
      !lea ecx, [edx + 0x400] ; ecx = $400 + rsum
      !imul eax, ecx          ; eax = ($400+rsum)*r*r
      !add [p.v_d], eax
      !movzx eax, byte [p.v_Color + 2]
      !movzx ecx, byte [p.v_c#i# + 2]
      !sub eax, ecx
      !imul eax, eax          ; eax = b*b
      !neg edx
      !add edx, 0x5fe         ; edx = $5fe - rsum
      !imul eax, edx          ; eax = ($5fe-rsum)*b*b
      !add eax, [p.v_d]
      !cmp eax, [p.v_bestd]
      !jnc nearestcolor.findnearest#i#_cont0
      !mov [p.v_bestd], eax
      !mov eax, [p.v_c#i#]
      !mov [p.v_c], eax
      !nearestcolor.findnearest#i#_cont0:
      mov rdx, *p#i
      add rdx, st
      mov *p#i, rdx
      mov eax, [rdx]
      !mov [p.v_c#i#], eax
      CompilerIf i = 1
         !jmp nearestcolor.findnearest0_loop
      CompilerElse
         !jmp nearestcolor.findnearest1_loop
      CompilerEndIf
      !nearestcolor.findnearest#i#_cont1:
      !mov dword [p.v_c#i#], 0
      !nearestcolor.findnearest#i#_cont2:
      CompilerIf i = 1
         !cmp dword [p.v_c0], 0
         !jnz nearestcolor.findnearest0_loop
      CompilerEndIf
   EndMacro
   
   !movzx eax, byte [p.v_Color + 1]
   !mov [p.v_d], eax
   *p1 = @Palette(IndexG(d)) : *p0 = *p1 - 4
   c0 = *p0\l : c1 = *p1\l
   M_FindNearest(0, -4)
   M_FindNearest(1, 4)
   DisableASM
   ProcedureReturn c
EndProcedure
Procedure FIT_CANVAS_TO_WINDOW()
   ; handle main window resize event
   ; resize canvas to fit window
   
   Protected ww = WindowWidth(#WinMain,#PB_Window_InnerCoordinate)
   Protected wh = WindowHeight(#WinMain,#PB_Window_InnerCoordinate) - MenuHeight()
   Protected iw = ImageWidth(#imgRef)
   Protected ih = ImageHeight(#imgRef)
   Protected cw, ch
   
   ResizeGadget(#Canvas, 0, 0, ww, wh-LinuxAdj) ;<-- -4 for Linux
   
    ;Pan_X = 0 : Pan_Y = 0
   If iw < ww : Pan_X = 0 : EndIf
   If ih < wh : Pan_Y = 0 : EndIf
   
    cw = GadgetWidth(#Canvas)
    ch = GadgetHeight(#Canvas)
   
    If iw > ww Or ih > wh
       oversize = #True
    Else
       oversize = #False
    EndIf
   
    ; set canvas cursor
    If oversize = #True
       CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
          SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor , #PB_Cursor_Hand)
       CompilerElse
          SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor , #PB_Cursor_Arrows)
       CompilerEndIf
    Else
       SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor , #PB_Cursor_Default)
    EndIf
   
    If ColorizeWinActive
       UPDATE_CANVAS(#imgAdj)
    Else
       UPDATE_CANVAS(#imgRef)
    EndIf

EndProcedure
Procedure FIT_WINDOW_TO_IMAGE(image.i)
   Protected iw = ImageWidth(image)
   Protected ih = ImageHeight(image)
   
   If GetWindowState(#WinMain) <> #PB_Window_Maximize
      If iw > CanvasMaxWidth  : iw = CanvasMaxWidth  : EndIf
      If iw < CanvasMinWidth  : iw = CanvasMinWidth  : EndIf
      If ih > CanvasMaxHeight : ih = CanvasMaxHeight : EndIf
      If ih < CanvasMinHeight : ih = CanvasMinHeight : EndIf
     
      ih + MenuHeight() + LinuxAdj ;<-- +4 for Linux
     
      ResizeWindow(#WinMain,(DW-iw-WinBorder_X)/2,(DH-ih-WinBorder_Y)/2,iw,ih)
   EndIf
   
   FIT_CANVAS_TO_WINDOW()

EndProcedure
Procedure HANDLE_WIN_COLORIZE_EVENTS(event.i)
   Static gadget, cp, cd, r, g, b, max, idx_s, idx_t, mpy.f
   Static dragSource, sourceImageID, dragTarget, mx, my, altered = #False
          
   Select event
      Case #PB_Event_CloseWindow
         If IsImage(#img_Tone) : FreeImage(#img_Tone) : EndIf
         If altered : UPDATE_CANVAS(#imgRef) : altered = 0 : EndIf
         RemoveKeyboardShortcut(#WinColorize, #PB_Shortcut_Return)
         ColorizeWinActive = #False
         
         If oversize = #True
            CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
               SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor , #PB_Cursor_Hand)
            CompilerElse
               SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor , #PB_Cursor_Arrows)
            CompilerEndIf
         Else
            SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor , #PB_Cursor_Default)
         EndIf
         
         CloseWindow(#WinColorize)
      Case #PB_Event_Menu
         If EventMenu() = #MenuItem_Enter
            gadget = GetActiveGadget()
            Select gadget
               Case #spin_thresh_1, #spin_thresh_2, #spin_thresh_3
                  SetGadgetState(gadget,Val(GetGadgetText(gadget)))
                  PostEvent(#PB_Event_Gadget,#WinColorize,GetActiveGadget(),#PB_EventType_Up)
            EndSelect
         EndIf
      Case #PB_Event_Gadget
         gadget = EventGadget()
         Select gadget
            Case #trk_palSize
               paletteSize = GetGadgetState(#trk_palSize)
               SetGadgetText(#Fra_palSize,Str(paletteSize * pMlt))
               If method = #opt_palette
                  cp = 1
                  cd = ElapsedMilliseconds() + 300
               EndIf
            Case #imgGad_C1, #imgGad_C2, #imgGad_C3, #imgGad_C4
               If EventType() = #PB_EventType_DragStart
                  dragSource  = gadget
                  sourceImageID = GetGadgetState(gadget)
                  DragPrivate(DragPrivateImage)
               ElseIf EventType() = #PB_EventType_LeftDoubleClick
                  Select gadget
                     Case #imgGad_C1 : Tcolor = tone(0)
                     Case #imgGad_C2 : Tcolor = tone(1)
                     Case #imgGad_C3 : Tcolor = tone(2)
                     Case #imgGad_C4 : Tcolor = tone(3)
                  EndSelect
                  T_COLOR_UPDATE()
               EndIf
            Case #ImgGad_Tone
               If EventType() = #PB_EventType_RightClick ; maximize luma
                  r = Red(Tcolor) : g = Green(Tcolor) : b = Blue(Tcolor)
                  max = r : If g > max : max = g : EndIf : If b > max : max = b : EndIf
                  mpy = 255.0 / max
                  r = Round(mpy * r,#PB_Round_Nearest)
                  g = Round(mpy * g,#PB_Round_Nearest)
                  b = Round(mpy * b,#PB_Round_Nearest)
                  Tcolor = RGB(r,g,b)
                  T_COLOR_UPDATE()
               ElseIf EventType() = #PB_EventType_LeftClick ; reduce luma
                  StartDrawing(ImageOutput(#img_Tone))
                     mx = WindowMouseX(#WinColorize)-GadgetX(#ImgGad_Tone)
                     my = WindowMouseY(#WinColorize)-GadgetY(#ImgGad_Tone)
                     Tcolor = Point(mx,my)
                  StopDrawing()
                  T_COLOR_UPDATE()
               EndIf
            Case #Combo_presets  : LOAD_PRESET()
            Case #btn_preview
               CopyImage(#imgRef,#ImgAdj)
               DisableGadget(#Btn_Apply,0)
               Select method
                  Case #opt_palette : ASSEMBLE_TO_PALETTE(#ImgAdj, Hue_Palette())
                  Case #opt_direct  : COLORIZE_IMAGE(#imgAdj)
                  Case #opt_blend   : COLORIZE_QUADTONE(#imgAdj)
               EndSelect
               UPDATE_CANVAS(#ImgAdj)
               altered = #True
            Case #btn_apply
               CopyImage(#ImgAdj,#imgRef)
               UPDATE_CANVAS(#imgRef)
               altered = #False
               PostEvent(#PB_Event_CloseWindow,#WinColorize,#PB_Ignore)
            Case #btn_close  : PostEvent(#PB_Event_CloseWindow,#WinColorize,#PB_Ignore)
            Case #Btn_source : UPDATE_CANVAS(#imgRef) : DisableGadget(#btn_apply,1)
            Case #btn_C1_set : tone(0) = Tcolor
               CreateImage(#img_C1,51,26,24,tone(0))
               SetGadgetState(#imgGad_C1,ImageID(#img_C1))
               If method = #opt_palette : DRAW_PALETTE(#opt_palette) : EndIf
            Case #btn_C2_set : tone(1) = Tcolor
               CreateImage(#img_C2,51,26,24,tone(1))
               SetGadgetState(#imgGad_C2,ImageID(#img_C2))
               If method = #opt_palette : DRAW_PALETTE(#opt_palette) : EndIf
            Case #btn_C3_set : tone(2) = Tcolor
               CreateImage(#img_C3,51,26,24,tone(2))
               SetGadgetState(#imgGad_C3,ImageID(#img_C3))
               If method = #opt_palette : DRAW_PALETTE(#opt_palette) : EndIf
            Case #btn_C4_set : tone(3) = Tcolor
               CreateImage(#img_C4,51,26,24,tone(3))
               SetGadgetState(#imgGad_C4,ImageID(#img_C4))
               If method = #opt_palette : DRAW_PALETTE(#opt_palette) : EndIf
            Case #opt_mono, #opt_duo, #opt_tri, #opt_quad
               UPDATE_TONEOPTION(gadget)
            Case #opt_direct, #opt_palette, #opt_blend
               UPDATE_METHOD(gadget)
            Case #spin_thresh_1, #spin_thresh_2, #spin_thresh_3
               SPIN_ADJUST(gadget)
            Case #chk_dither
               dither = GetGadgetState(#chk_dither)
         EndSelect
      Case  #PB_Event_GadgetDrop
         If EventDropType() = #PB_Drop_Private
            If EventDropPrivate() = DragPrivateImage
               dragTarget = EventGadget()
               SetGadgetState(dragSource, GetGadgetState(dragTarget))
               Delay(50)
               While WindowEvent() : Wend
               Delay(50)
               SetGadgetState(dragTarget, sourceImageID)
               Select dragSource
                  Case #imgGad_C1 : idx_s = 0
                  Case #imgGad_C2 : idx_s = 1
                  Case #imgGad_C3 : idx_s = 2
                  Case #imgGad_C4 : idx_s = 3
               EndSelect
               Select dragTarget
                  Case #imgGad_C1 : idx_t = 0
                  Case #imgGad_C2 : idx_t = 1
                  Case #imgGad_C3 : idx_t = 2
                  Case #imgGad_C4 : idx_t = 3
               EndSelect
               Swap tone(idx_s), tone(idx_t)
            EndIf
         EndIf
   EndSelect
   
   If cp = #True
      If ElapsedMilliseconds() > cd
         cp = #False
         DRAW_PALETTE(method)
      EndIf
   EndIf
   
EndProcedure
Procedure HANDLE_WIN_MAIN_EVENTS(event.i)
   Static F$
   Select event
      Case #PB_Event_CloseWindow
         endProgram = #True
      Case #PB_Event_SizeWindow     : FIT_CANVAS_TO_WINDOW()
      Case #PB_Event_RestoreWindow  : FIT_WINDOW_TO_IMAGE(#imgRef)
      Case #PB_Event_RightClick
         If ColorizeWinActive = #False
            DisplayPopupMenu(#PopMenu,WindowID(#WinMain))
         EndIf
      Case #PB_Event_Menu
         If ColorizeWinActive = #False
            Select EventMenu()
               Case #MenuItem_Load     : LOAD_IMAGE()
               Case #MenuItem_Save     : SAVE_IMAGE()
               Case #MenuItem_Copy     : COPY()
               Case #MenuItem_Paste    : PASTE()
               Case #MenuItem_Exit     : endProgram = #True
               Case #MenuItem_Colorize : OPEN_COLORIZE_WINDOW()
               Case #MenuItem_Revert   : REVERT()
            EndSelect
         EndIf
      Case #PB_Event_Gadget
         Select EventGadget()
               
         EndSelect
      Case  #PB_Event_GadgetDrop
         If EventDropType() = #PB_Drop_Files
            F$  = EventDropFiles()
            If LoadImage(#imgRef, F$)
               File$  = F$ ; for global use
               CurrentFileName$ = GetFilePart(F$,#PB_FileSystem_NoExtension) ; for global use
               CopyImage(#imgRef,#imgRevert)
               Pan_X = 0 : Pan_Y = 0
               F$ = GetFilePart(File$) + "  |  (" + ImageWidth(#imgRef) + " x " + ImageHeight(#imgRef) + ")"
               SetWindowTitle(#WinMain,Title$ + "| " + F$)
               FIT_WINDOW_TO_IMAGE(#imgRef)
            EndIf
         EndIf
   EndSelect
EndProcedure
Procedure LOAD_IMAGE()
   Static Path$, pattern, firstRun = #True
   Protected Pattern$ = "all supported formats|*.png;*.jpg;*.jpeg;*.jp2;;*.bmp|*.png|*.png|*.jpg, *.jpeg"+
                        "|*.jpg;*.jpeg|*.jpeg2000|*.jp2|*.bmp|*.bmp|all files|*.*"
   Protected F$, text$, h, w, aw, ah, x, y
   
   If firstRun = #True : firstRun = #False
      Path$ = GetPathPart(File$)
   EndIf

   F$ = OpenFileRequester("Select image to process", Path$, Pattern$, pattern)
   
   If F$
      File$ = F$ ; for global use
      pattern = SelectedFilePattern()
      CurrentFileName$ = GetFilePart(F$,#PB_FileSystem_NoExtension) ; for global use
      Path$ = GetPathPart(F$)
     
      If LoadImage(#imgRef, F$)
         CopyImage(#imgRef,#imgRevert)
         Pan_X = 0 : Pan_Y = 0
         F$ = GetFilePart(File$) + "  |  (" + ImageWidth(#imgRef) + " x " + ImageHeight(#imgRef) + ")"
         SetWindowTitle(#WinMain,Title$ + "| " + F$)
         FIT_WINDOW_TO_IMAGE(#imgRef)
      EndIf
   EndIf
   
EndProcedure
Procedure LOAD_PRESET()
   
   If EventType() = #PB_EventType_Change
      preset = GetGadgetState(#Combo_presets)
      SelectElement(presetList(),preset)
      tone(0) = presetList()\C1
      tone(1) = presetList()\C2
      tone(2) = presetList()\C3
      tone(3) = presetList()\C4
      CreateImage(#img_C1,51,26,24,tone(0))
      SetGadgetState(#imgGad_C1,ImageID(#img_C1))
      CreateImage(#img_C2,51,26,24,tone(1))
      SetGadgetState(#imgGad_C2,ImageID(#img_C2))
      CreateImage(#img_C3,51,26,24,tone(2))
      SetGadgetState(#imgGad_C3,ImageID(#img_C3))
      CreateImage(#img_C4,51,26,24,tone(3))
      SetGadgetState(#imgGad_C4,ImageID(#img_C4))
      
      If method = #opt_palette : DRAW_PALETTE(method) : EndIf
   EndIf
   
EndProcedure
Procedure OPEN_COLORIZE_WINDOW()
   Protected result,  R, G, B, x, y
   Static firstRun = #True, flags = #PB_Window_Tool|#PB_Window_SystemMenu

   x = WindowX(#WinMain,#PB_Window_InnerCoordinate)
   y = WindowY(#WinMain,#PB_Window_InnerCoordinate)
   
   result = OpenWindow(#WinColorize,x,y,600,320,"Colorize image",flags,WindowID(#WinMain))
   
   If result
      SetWindowColor(#WinColorize,$7E940A)
      ColorizeWinActive = #True
      DragPrivateImage = WindowID(#WinMain)
      
      AddKeyboardShortcut(#WinColorize, #PB_Shortcut_Return, #MenuItem_Enter)
      SetGadgetAttribute(#Canvas,#PB_Canvas_Cursor,#PB_Cursor_Cross)
      
      CreateImage(#img_Hue,1536,20)
      CreateImage(#img_Adj_R,256,20)
      CreateImage(#img_Adj_G,256,20)
      CreateImage(#img_Adj_B,256,20)
      CreateImage(#img_C1,51,26,24,tone(0))
      CreateImage(#img_C2,51,26,24,tone(1))
      CreateImage(#img_C3,51,26,24,tone(2))
      CreateImage(#img_C4,51,26,24,tone(3))
     
      R = $FF : G = 0 : B = 0 : x = 0 ;make hue image + fill hue() array
      StartDrawing(ImageOutput(#img_Hue))
         MakeHueGradient(G, R)
         MakeHueGradient(B, G)
         MakeHueGradient(R, B)
      StopDrawing()
     
      ResizeImage(#img_Hue,530,20)
     
      DRAW_GRAD(#img_Adj_R,0)
      DRAW_GRAD(#img_Adj_G,8)
      DRAW_GRAD(#img_Adj_B,16)
     
      FrameGadget(#Fra_Hue,5,5,540,50,"HUE | FFFF00")
      CanvasGadget(#Can_Hue,10,25,530,20,#PB_Canvas_ClipMouse)
      BindGadgetEvent(#Can_Hue,@COLORIZE_CANVAS_CALLBACK())
     
      FrameGadget(#Fra_Modify,5,60,266,120,"Modify | " + RSet(Hex(Tcolor),6,"0"))
      CanvasGadget(#Can_Adj_R,10,150,256,20,#PB_Canvas_ClipMouse)
      CanvasGadget(#Can_Adj_G,10,115,256,20,#PB_Canvas_ClipMouse)
      CanvasGadget(#Can_Adj_B,10,080,256,20,#PB_Canvas_ClipMouse)
      BindGadgetEvent(#Can_Adj_R,@COLORIZE_CANVAS_CALLBACK())
      BindGadgetEvent(#Can_Adj_G,@COLORIZE_CANVAS_CALLBACK())
      BindGadgetEvent(#Can_Adj_B,@COLORIZE_CANVAS_CALLBACK())
     
      FrameGadget(#Fra_Method,5,185,70,130,"Method")
      OptionGadget(#opt_direct,10,205,60,22,"Abrupt")
      OptionGadget(#opt_blend,10,232,60,22,"Blend")
      OptionGadget(#opt_palette,10,259,60,22,"Palette")
      CheckBoxGadget(#chk_dither,10,286,60,22,"Dither")
      SetGadgetState(#chk_dither,dither)
     
      FrameGadget(-1,80,185,70,130,"Tones")
      OptionGadget(#opt_mono,85,205,60,22,"One")   : GadgetToolTip(#opt_mono,"C1")
      OptionGadget(#opt_duo ,85,232,60,22,"Two")   : GadgetToolTip(#opt_duo ,"C1, C2")
      OptionGadget(#opt_tri ,85,259,60,22,"Three") : GadgetToolTip(#opt_tri ,"C1, C2, C3")
      OptionGadget(#opt_quad,85,286,60,22,"Four")  : GadgetToolTip(#opt_quad,"C1, C2, C3, C4")
     
      FrameGadget(#Fra_C1      ,255,185,65,87,"C1")
      ImageGadget(#imgGad_C1   ,260,205,51,026,ImageID(#img_C1),#PB_Image_Border)
      EnableGadgetDrop(#imgGad_C1,#PB_Drop_Private,#PB_Drag_Copy,DragPrivateImage)
      GadgetToolTip(#imgGad_C1,"double click to edit")
      ButtonGadget(#btn_C1_set ,260,240,55,025,"set")
      GadgetToolTip(#btn_C1_set,"set color 1")
     
      FrameGadget(#Fra_C2      ,330,185,65,87,"C2")
      ImageGadget(#imgGad_C2   ,335,205,51,026,ImageID(#img_C2),#PB_Image_Border)
      EnableGadgetDrop(#imgGad_C2,#PB_Drop_Private,#PB_Drag_Copy,DragPrivateImage)
      GadgetToolTip(#imgGad_C2 ,"double click to edit")
      ButtonGadget(#btn_C2_set ,335,240,55,025,"set")
      GadgetToolTip(#btn_C2_set,"set color 2")
     
      FrameGadget(#Fra_C3      ,405,185,65,87,"C3")
      ImageGadget(#imgGad_C3   ,410,205,51,026,ImageID(#img_C3),#PB_Image_Border)
      EnableGadgetDrop(#imgGad_C3,#PB_Drop_Private,#PB_Drag_Copy,DragPrivateImage)
      GadgetToolTip(#imgGad_C3 ,"double click to edit")
      ButtonGadget(#btn_C3_set ,410,240,55,025,"set")
      GadgetToolTip(#btn_C3_set,"set color 3")
     
      FrameGadget(#Fra_C4,480,185,65,87,"C4")
      ImageGadget(#imgGad_C4,485,205,51,26,ImageID(#img_C4),#PB_Image_Border)
      EnableGadgetDrop(#imgGad_C4,#PB_Drop_Private,#PB_Drag_Copy,DragPrivateImage)
      GadgetToolTip(#imgGad_C4 ,"double click to edit")
      ButtonGadget(#btn_C4_set, 485,240,55,25,"set")
      GadgetToolTip(#btn_C4_set,"set color 4")
     
      SpinGadget(#spin_thresh_1,295,280,55,30,0,256)
      SpinGadget(#spin_thresh_2,370,280,55,30,0,255)
      SpinGadget(#spin_thresh_3,445,280,55,30,0,254)
      GadgetToolTip(#spin_thresh_1,"1|2 threshold")
      GadgetToolTip(#spin_thresh_2,"2|3 threshold")
      GadgetToolTip(#spin_thresh_3,"3|4 threshold")
     
      ButtonGadget(#btn_preview,160,190,85,25,"Preview")
      ButtonGadget(#Btn_source ,160,220,85,25,"Source")
      ButtonGadget(#btn_apply  ,160,250,85,25,"Apply") : DisableGadget(#btn_apply,1)
      ButtonGadget(#btn_close  ,510,280,85,30,"Close")
      GadgetToolTip(#Btn_source,"show source image")
      
      ComboBoxGadget(#Combo_presets,160,280,125,30)
      GadgetToolTip(#Combo_presets,"load preset")
      ForEach presetList()
         AddGadgetItem(#Combo_presets,-1,presetList()\na)
      Next
      
      SetGadgetState(#Combo_presets,preset)
         
      If firstRun : firstRun = #False
         PostEvent(#PB_Event_Gadget,#WinColorize,#Combo_presets,#PB_EventType_Change)
      EndIf
      
      FrameGadget(#Fra_palSize, 550,05,45,266,Str(paletteSize * pMlt))
      TrackBarGadget(#trk_palSize,558,25,30,236,2,64,#PB_TrackBar_Vertical)
      GadgetToolTip(#trk_palSize,"palette size")
      SetGadgetState(#trk_palSize,paletteSize)
      DisableGadget(#trk_palSize,1)
     
      SetGadgetState(toneOption,1)
      SetGadgetState(method,1)
     
      SetGadgetData(#Can_Hue,HueSet)          : REDRAW_CUSTOM_TRACKBAR(#Can_Hue)
      SetGadgetData(#Can_Adj_R,Red(Tcolor))   : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_R)
      SetGadgetData(#Can_Adj_G,Green(Tcolor)) : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_G)
      SetGadgetData(#Can_Adj_B,Blue(Tcolor))  : REDRAW_CUSTOM_TRACKBAR(#Can_Adj_B)
     
      FrameGadget(#Fra_Grad,280,60,266,120,"Gradient or Palette")
      CreateImage(#img_Tone,256,100,24,0)
      ImageGadget(#ImgGad_Tone,285,75,256,100,ImageID(#Img_Tone))
      GadgetToolTip(#ImgGad_Tone,"click: right = maximize, left = darken")
     
      UPDATE_TONEOPTION(toneOption)
      UPDATE_METHOD(method)
      
      DRAW_PALETTE(method)
      CopyImage(#imgRef,#ImgAdj)
      
      ProcedureReturn result
   EndIf
EndProcedure
Procedure OPEN_MAIN_WINDOW()
   Protected result, w = 600, h = 450
   Protected flags = #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_SizeGadget |
                     #PB_Window_MaximizeGadget | #PB_Window_ScreenCentered
   
   result = OpenWindow(#WinMain,0,0,w,h,Title$,flags)
   If result
     
      ExamineDesktops()
      DW = DesktopWidth(0)
      DH = DesktopHeight(0)
     
      CanvasMinHeight = h
      CanvasMinWidth = w
     
      WinBorder_X = WindowWidth(#WinMain,#PB_Window_FrameCoordinate) - WindowWidth(#WinMain)
      WinBorder_Y = WindowHeight(#WinMain,#PB_Window_FrameCoordinate) - WindowHeight(#WinMain)
     
      CompilerIf #PB_Compiler_OS = #PB_OS_Linux
         LinuxAdj = 4
         h + MenuHeight() + LinuxAdj
      CompilerEndIf
     
      WindowBounds(#WinMain,w,h,#PB_Ignore,#PB_Ignore)
     
      CreateMenu(#MainMenu,WindowID(#WinMain))
      MenuTitle("  File  ")
         MenuItem(#MenuItem_Load,"Load image" + Chr(9) + "Ctrl+L")
         MenuItem(#MenuItem_Save,"Save image" + Chr(9) + "Ctrl+S")
         MenuBar()
         MenuItem(#MenuItem_Exit,"Exit")
     
      MenuTitle("  Edit  ")
         MenuItem(#MenuItem_Colorize,"Colorize image...")
         MenuBar()
         MenuItem(#MenuItem_Copy,"Copy to clipboard" + Chr(9) + "Ctrl+C")
         MenuItem(#MenuItem_Paste,"Paste from clipboard" + Chr(9) + "Ctrl+V")
         MenuBar()
         MenuItem(#MenuItem_Revert,"Revert" + Chr(9) + "Ctrl+R")
     
      CreatePopupMenu(#PopMenu)
      OpenSubMenu("File...")
         MenuItem(#MenuItem_Load,"Load image" + Chr(9) + "Ctrl+L")
         MenuItem(#MenuItem_Save,"Save image" + Chr(9) + "Ctrl+S")
         MenuBar()
         MenuItem(#MenuItem_Exit,"Exit")
      CloseSubMenu()
   
      MenuBar()
         OpenSubMenu("Edit...")
            MenuItem(#MenuItem_Colorize,"Colorize image...")
            MenuBar()
            MenuItem(#MenuItem_Copy,"Copy to clipboard" + Chr(9) + "Ctrl+C")
            MenuItem(#MenuItem_Paste,"Paste from clipboard" + Chr(9) + "Ctrl+V")
            MenuBar()
            MenuItem(#MenuItem_Revert,"Revert" + Chr(9) + "Ctrl+R")
         CloseSubMenu()
     
      AddKeyboardShortcut(#WinMain, #PB_Shortcut_L | #PB_Shortcut_Control , #MenuItem_Load)
      AddKeyboardShortcut(#WinMain, #PB_Shortcut_S | #PB_Shortcut_Control , #MenuItem_Save)
      AddKeyboardShortcut(#WinMain, #PB_Shortcut_C | #PB_Shortcut_Control , #MenuItem_Copy)
      AddKeyboardShortcut(#WinMain, #PB_Shortcut_V | #PB_Shortcut_Control , #MenuItem_Paste)
      AddKeyboardShortcut(#WinMain, #PB_Shortcut_R | #PB_Shortcut_Control , #MenuItem_Revert)
     
      CanvasMaxWidth  = DW - WinBorder_X
      CanvasMaxHeight = DH - WinBorder_Y - MenuHeight()
     
      h - MenuHeight() - LinuxAdj
     
      CanvasGadget(#Canvas,0,0,w,h,#PB_Canvas_ClipMouse)
      BindGadgetEvent(#Canvas,@CANVAS_CALLBACK())
      EnableGadgetDrop(#Canvas,#PB_Drop_Files,#PB_Drag_Copy)
      CreateImage(#imgRef,w,h,24,#CBcolor)
      UPDATE_CANVAS(#imgRef)
     
      CompilerIf #PB_Compiler_OS = #PB_OS_Windows
         File$ = GetHomeDirectory() + "Pictures\"
      CompilerElse
         File$ = GetHomeDirectory() + "Pictures/"
      CompilerEndIf
     
      File$ + "no_image_loaded"
      SetWindowTitle(#WinMain,Title$ + "| " + "no_image_loaded")
   EndIf
   
   ProcedureReturn result
EndProcedure
Procedure PASTE()
   Protected F$
   If GetClipboardImage(#imgRef,32)
      CopyImage(#imgRef, #imgRevert)
      CurrentFileName$ = "NewImage"
      File$ = GetPathPart(File$) + CurrentFileName$
      F$ = CurrentFileName$ + "  |  (" + ImageWidth(#imgRef) + " x " + ImageHeight(#imgRef) + ")"
      SetWindowTitle(#WinMain,Title$ + "| " + F$)
      FIT_WINDOW_TO_IMAGE(#imgRef)
   EndIf
EndProcedure
Procedure.l PointOrdDith(x.i, y.i)
   ;this procedure was borrowed from Wilbert's Neuquant module
   EnableASM
   Point(x, y)
   !movd xmm0, eax
   !mov al, [p.v_x]
   !mov ah, [p.v_y]
   !shl al, 6
   !and eax, 0x3c0
   !shr ax, 3
   !punpcklbw xmm0, xmm0
   CompilerIf #x64
      !lea rdx, [nearest.l_pointorddith]
      !movq xmm1, [rdx + rax]
   CompilerElse
      !movq xmm1, [nearest.l_pointorddith + eax]
   CompilerEndIf
   !psrlw xmm0, 8
   !paddw xmm0, xmm1
   !packuswb xmm0, xmm0
   !movd eax, xmm0
   ProcedureReturn
   !nearest.l_pointorddith:
   !dq 0xfff9fff9fff9, 0x000100010001, 0xfffbfffbfffb, 0x000300030003
   !dq 0x000500050005, 0xfffdfffdfffd, 0x000700070007, 0xffffffffffff
   !dq 0xfffcfffcfffc, 0x000400040004, 0xfffafffafffa, 0x000200020002
   !dq 0x000800080008, 0x000000000000, 0x000600060006, 0xfffefffefffe
   DisableASM
EndProcedure 
Procedure REDRAW_CUSTOM_TRACKBAR(canvas.i)
   Protected cxor.i, image.i, position.i = GetGadgetData(canvas)
   
   Select canvas
      Case #Can_Hue   : image = #img_Hue   : cxor = $FFFFFF
      Case #Can_Adj_R : image = #img_Adj_R : cxor = $00FFFF
      Case #Can_Adj_G : image = #img_Adj_G : cxor = $FF00FF
      Case #Can_Adj_B : image = #img_Adj_B : cxor = $00FFFF
   EndSelect
   
   StartDrawing(CanvasOutput(canvas))
      DrawImage(ImageID(image),0,0)
      DrawingMode(#PB_2DDrawing_XOr)
      Box(position-1,0,3,20,cxor)
   StopDrawing()
   
EndProcedure
Procedure REVERT()
   If IsImage(#imgRevert)
      CopyImage(#imgRevert,#imgRef)
      UPDATE_CANVAS(#imgRef)
   EndIf
EndProcedure
Procedure SAVE_IMAGE()
   Static YesNo = #PB_MessageRequester_YesNo, Yes = #PB_MessageRequester_Yes
   Static Pattern$ = "image.png|*.png|image.jpg|*.jpg|image.jpeg|*.jpeg|image.jpeg2000|*.jp2|image.bmp|*.bmp"
   Static lastPattern = 1, firstRun = #True
   Protected F$, p, cancel = #False
   
   Select LCase(GetExtensionPart(File$))
      Case "png" : lastPattern = 0
      Case "jpg" : lastPattern = 1
      Case "jpeg": lastPattern = 2
      Case "jpeg2000" : lastPattern = 3
      Case "bmp" : lastPattern = 4
   EndSelect
   
   F$ = SaveFileRequester("Save image",  GetPathPart(File$)+CurrentFileName$, Pattern$, lastPattern)
   
   If F$
      lastPattern = SelectedFilePattern()
     
      ; remove any existing file extension
      p = FindString(ReverseString(F$),".")
      If p
         F$ = Left(F$,Len(F$)-p)
      EndIf
     
      Select SelectedFilePattern()
         Case 0 : F$ + ".png"
         Case 1 : F$ + ".jpg"
         Case 2 : F$ + ".jpeg"
         Case 3 : F$ + ".jp2"
         Case 4 : F$ + ".bmp"
      EndSelect
     
      If FileSize(F$) <> -1 ; file exists
         If MessageRequester("File Exists!", "Do you wish to overwrite?", YesNo) <> Yes
            cancel = #True
         EndIf
      EndIf
     
      If cancel = #False
         Select SelectedFilePattern()
            Case 0 : SaveImage(#imgRef, F$, #PB_ImagePlugin_PNG)
            Case 1 : SaveImage(#imgRef, F$, #PB_ImagePlugin_JPEG,8)
            Case 2 : SaveImage(#imgRef, F$, #PB_ImagePlugin_JPEG,8)
            Case 3 : SaveImage(#imgRef, F$, #PB_ImagePlugin_JPEG2000,3)
            Case 4 : SaveImage(#imgRef, F$, #PB_ImagePlugin_BMP)
         EndSelect
         
         File$ = F$ ; for global use
         F$ = GetFilePart(File$) + "  |  (" + ImageWidth(#imgRef) + " x " + ImageHeight(#imgRef) + ")"
         SetWindowTitle(#WinMain,Title$ + "| " + F$)
      EndIf
   EndIf
EndProcedure
Procedure SPIN_ADJUST(gadget)
   Protected value.i
   
   Select EventType()
      Case #PB_EventType_Change
         SetGadgetState(gadget,Val(GetGadgetText(gadget)))
      Case #PB_EventType_Up, #PB_EventType_Down
         value = GetGadgetState(gadget)
         Select gadget
            Case #spin_thresh_1
               If value <= GetGadgetState(#spin_thresh_2)
                  value =GetGadgetState(#spin_thresh_2)+1
               EndIf
               SM1 = value
            Case #spin_thresh_2
               If value <= GetGadgetState(#spin_thresh_3)
                  value = GetGadgetState(#spin_thresh_3)+1
               ElseIf value >= GetGadgetState(#spin_thresh_1)
                  value = GetGadgetState(#spin_thresh_1)-1
               EndIf
               SM2 = value
            Case #spin_thresh_3
               If value >= GetGadgetState(#spin_thresh_2)
                  value = GetGadgetState(#spin_thresh_2)-1
               EndIf
               SM3 = value
         EndSelect
         
         SetGadgetText(gadget,Str(value))
         SetGadgetState(gadget,value)
   EndSelect
EndProcedure
Procedure T_COLOR_UPDATE()
   SetGadgetData(#Can_Adj_R,Red(Tcolor))
   SetGadgetData(#Can_Adj_G,Green(Tcolor))
   SetGadgetData(#Can_Adj_B,Blue(Tcolor))
   SetGadgetText(#Fra_Modify,"Modify | " + RSet(Hex(Tcolor),6,"0"))
   REDRAW_CUSTOM_TRACKBAR(#Can_Adj_R)
   REDRAW_CUSTOM_TRACKBAR(#Can_Adj_G)
   REDRAW_CUSTOM_TRACKBAR(#Can_Adj_B)
   DRAW_PALETTE(#opt_direct)
EndProcedure
Procedure UPDATE_CANVAS(image.i)
   Protected x, y, iw, ih, ow, oh, Llim, Rlim, Tlim, Blim, text$
   
   If IsImage(image)
      StartDrawing(CanvasOutput(#Canvas))
         ow = OutputWidth()  : iw = ImageWidth(image)
         oh = OutputHeight() : ih = ImageHeight(image)
         
         Box(0,0,ow,oh,#CBcolor)
         
         x = (ow - iw) / 2 + Pan_X
         y = (oh - Ih) / 2 + Pan_Y
         
         DrawImage(ImageID(image),x,y)
      StopDrawing()
   EndIf
EndProcedure
Procedure UPDATE_METHOD(gadget.i)
   
   If method <> #opt_palette And gadget = #opt_palette
      DRAW_PALETTE(#opt_palette)
   ElseIf method = #opt_palette And gadget <> #opt_palette
      DRAW_PALETTE(gadget)
   EndIf
   
   method = gadget
   
   If method = #opt_blend
      If GetGadgetState(#opt_mono)
         SetGadgetState(#opt_mono,0)
         SetGadgetState(#opt_duo, 1)
         toneOption = #opt_duo
         UPDATE_TONEOPTION(toneOption)
      EndIf
   EndIf
   
   If method = #opt_direct
      UPDATE_TONEOPTION(toneOption)
   Else
      DisableGadget(#spin_thresh_1, 1) : DisableGadget(#spin_thresh_2, 1) : DisableGadget(#spin_thresh_3, 1)
   EndIf
   
   DisableGadget(#opt_mono,Bool(method = #opt_blend))
   DisableGadget(#chk_dither,Bool(method = #opt_blend))
   DisableGadget(#trk_palSize,Bool(method <> #opt_palette))

EndProcedure
Procedure UPDATE_TONEOPTION(gadget.i)
   Protected S2, S3, S4, L1, L2, L3
   
   toneOption = gadget
   
      Select gadget
         Case #opt_mono : L1  = 0 : L2 = 0   : L3 = 0
            S2 = 1 : S3 = 1 : S4 = 1
         Case #opt_duo  : L1 = SM1 : L2 = 0  : L3 = 0
            S2 = 0 : S3 = 1 : S4 = 1
         Case #opt_tri  : L1 = SM1 : L2 = SM2 : L3 = 0
            S2 = 0 : S3 = 0 : S4 = 1
         Case #opt_quad : L1 = SM1 : L2 = SM2 : L3 = SM3
            S2 = 0 : S3 = 0 : S4 = 0
      EndSelect
      
   DisableGadget(#btn_C2_set,S2) : DisableGadget(#btn_C3_set,S3) : DisableGadget(#btn_C4_set,S4)
   DisableGadget(#imgGad_C2,S2)  : DisableGadget(#imgGad_C3,S3)  : DisableGadget(#imgGad_C4,S4)
   
   If method = #opt_palette
      DRAW_PALETTE(#opt_palette)
      SetGadgetText(#Fra_palSize,Str(paletteSize * pMlt))
      ProcedureReturn
   ElseIf method = #opt_direct
      SetGadgetState(#spin_thresh_1,L1)     : SetGadgetState(#spin_thresh_2,L2)     : SetGadgetState(#spin_thresh_3,L3)
      SetGadgetText(#spin_thresh_1,Str(L1)) : SetGadgetText(#spin_thresh_2,Str(L2)) : SetGadgetText(#spin_thresh_3,Str(L3))
      DisableGadget(#spin_thresh_1, S2)     : DisableGadget(#spin_thresh_2, S3)     : DisableGadget(#spin_thresh_3, S4)
   EndIf
EndProcedure

;{ Preset Color data
DataSection
   PresetData:
   Data.s "gray scale"        : Data.i $FFFFFF, $B0B0B0, $707070, $303030
   Data.s "aqua"              : Data.i $FFEA00, $FFBF00, $FF9000, $FF5C00
   Data.s "blond"             : Data.i $BFFDFF, $A3FDFF, $96E3FF, $76D5FF
   Data.s "brunette"          : Data.i $BDDCFB, $7EACF9, $697AE9, $3F435B
   Data.S "candy"             : Data.i $4AC5FF, $005FFF, $E08E00, $FF5DB7
   Data.s "Cinnabar"          : Data.i $76BCFF, $2A3AEF, $D4C343, $0010CF
   Data.s "grass"             : Data.i $BFFFD9, $77FFD0, $82FFA8, $D2FFC3
   Data.s "magic"             : Data.i $7FE1FF, $D25BFF, $D7D700, $FFFFFF
   Data.s "rainbow"           : Data.i $00FFFF, $FFFF00, $FF00FF, $0000FF
   Data.s "rose"              : Data.i $A228FF, $A228DA, $FF0060, $4611A3
   Data.s "skin"              : Data.i $8EBFFF, $76B0FF, $5E8DFF, $517AE6
   Data.s "tangerine"         : Data.i $00DCFF, $00AEFF, $0085FF, $0059FF
   Data.s "topaz"             : Data.i $7CC8FF, $66A4E7, $4E85CA, $3265A7
   Data.s "warrior"           : Data.i $A3C2FF, $FFBF00, $4000FF, $FF5C00
   Data.s "End_Data"
EndDataSection : ;}
Last edited by BasicallyPure on Mon Sep 19, 2016 6:23 pm, edited 10 times in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Colorize an image

Post by wilbert »

Nice that you are still working on it.
The interface seems to work fine :)

I started experimenting a bit with OpenGL for image processing; see if it is a complicated thing.
I never worked with OpenGL before and so far it's a lot of trial and error to make any progress.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Colorize an image

Post by BasicallyPure »

I found a bug that affects Linux users only.
The problem relates to loading the preset colors.
I have updated the code above to fix the problem.
However, if you don't want to recopy the entire code you
can make this small change.

in the procedure OPEN_PRESET_WINDOW() change the following code

Code: Select all

               Else
                  CloseWindow(#WinPreset) : Break
               EndIf
to this,

Code: Select all

               ElseIf EventWindow() <> #WinMain
                  CloseWindow(#WinPreset)
                  Break
               EndIf
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Fred
Administrator
Administrator
Posts: 18244
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Colorize an image

Post by Fred »

You can just edit your previous post to do the change
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Colorize an image

Post by BasicallyPure »

My previous code did not fully realize my original goal but now that has been achieved.
Using Wilbert's ColorizeImageSMH() procedure as a starting point I have added a new feature
that allows smooth blending between colors transitions when colorizing an image.
This new method works optionally with two, three, or four smoothly blended colors.

The user interface for colorizing has been redesigned and new features added.
Drag and drop is now supported to swap colors between C1, C2, C3, and C4.
Presets are now directly selectable from a ComboBoxGadget.
There were minor bug fixes as well.

I have updated the code above so you can scroll up or click here.

Here is an example of colorizing a grayscale image using the 'blend' method with four colors.
Image
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
Post Reply