I was slightly wrong about the color space - the best space to work in is HSL, which is similar to HSV but provides more explicit control of a color's "whiteness". Since this is a multiply filter, the closer to white the tint color is the less it will affect the target image. Here is an example of how to use an HSL color transform to vary the intensity of the tint color by changing the lightness channel:
Code: Select all
; save this file as tint.pbi
;====================================================
; Image tinting and HSL<->RGB color transformation
; ©2008 By Roger Hyde. All rights reserved
;-------------------------------------------------------------------
; This software is distributed in the hope that it will be
; useful, but With NO WARRANTY OF ANY KIND.
; No author or distributor accepts responsibility to anyone for the
; consequences of using this software, or for whether it serves any
; particular purpose or works at all, unless he or she says so in
; writing. Everyone is granted permission to copy, modify And
; redistribute this source code, For commercial or non-commercial
; purposes, with the following restrictions: (1) the origin of this
; source code must not be misrepresented; (2) modified versions must
; be plainly marked As such; and (3) this notice may not be removed
; or altered from any source or modified source distribution.
;-------------------------------------------------------------------
; Thanks go to Rescator for the min/max macros :o)
;====================================================
Structure HSLColor
H.f
S.f
L.f
EndStructure
Macro _rmul(a,b)
(Red(a) * (Red(b) / 255.0))
EndMacro
Macro _gmul(a,b)
(Green(a) * (Green(b) / 255.0))
EndMacro
Macro _bmul(a,b)
(Blue(a) * (Blue(b) / 255.0))
EndMacro
Macro _mul(colora, colorb)
RGB(_rmul(colora,colorb), _gmul(colora,colorb), _bmul(colora,colorb))
EndMacro
Macro Min(a,b)
((Not a<b)*b)|((Not b<a)*a)
EndMacro
Procedure.l Min3(a, b, c)
temp = Min(a, b)
ProcedureReturn Min(temp, c)
EndProcedure
Procedure.f MinF(a.f, b.f)
If a < b
ProcedureReturn a
Else
ProcedureReturn b
EndIf
EndProcedure
Procedure.f MaxF(a.f, b.f)
If a > b
ProcedureReturn a
Else
ProcedureReturn b
EndIf
EndProcedure
Procedure.f Min3F(a.f, b.f, c.f)
ProcedureReturn MinF(MinF(a, b), MinF(b, c))
EndProcedure
Procedure.f Max3F(a.f, b.f, c.f)
ProcedureReturn MaxF(MaxF(a, b), MaxF(b, c))
EndProcedure
Procedure.l HSL2RGB(*hsl.HSLColor)
Protected r.f, g.f, b.f, h.f, s.f, l.f
Protected temp1.f, temp2.f
Dim t3.d(2)
Dim clr.d(2)
h = *hsl\H / 360.0
s = *hsl\S / 100.0
l = *hsl\L / 100.0
If l = 0
r = 0 : g = 0 : b = 0
Else
If s = 0
r = l : g = l : b = l
Else
If l <= 0.5
temp2 = l * (1.0 + s)
Else
temp2 = l + s - (l * s)
EndIf
temp1 = 2.0 * l - temp2
t3(0) = h + 1.0 / 3.0
t3(1) = h
t3(2) = h - 1.0 / 3.0
For i = 0 To 2
If t3(i) < 0 : t3(i) + 1.0 : EndIf
If t3(i) > 1 : t3(i) - 1.0 : EndIf
If 6.0 * t3(i) < 1.0
clr(i) = temp1 + (temp2 - temp1) * t3(i) * 6.0
ElseIf 2.0 * t3(i) < 1.0
clr(i) = temp2
ElseIf 3.0 * t3(i) < 2.0
clr(i) = (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - t3(i)) * 6.0)
Else
clr(i) = temp1
EndIf
Next
r = clr(0)
g = clr(1)
b = clr(2)
EndIf
EndIf
ProcedureReturn RGB(Min3((r*255), 255, 255), Min3((g*255), 255, 255), Min3((b*255), 255, 255))
EndProcedure
Procedure RGB2HSL(color.l, *hsl.HSLColor)
r.f = Red(color) / 255.0
g.f = Green(color) / 255.0
b.f = Blue(color) / 255.0
min.f = Min3F(r, g, b)
max.f = Max3F(r, g, b)
delta.f = max - min
*hsl\L = (max + min) / 2.0
If delta = 0
*hsl\H = 0.0
*hsl\S = 0.0
Else
If *hsl\L < 0.5
*hsl\S = delta / (max + min)
Else
*hsl\S = delta / (2.0 - delta)
EndIf
dr.f = (((max - r) / 6.0) + (delta / 2.0)) / delta
dg.f = (((max - g) / 6.0) + (delta / 2.0)) / delta
db.f = (((max - b) / 6.0) + (delta / 2.0)) / delta
If r = max
*hsl\H = db - dg
ElseIf g = max
*hsl\H = (1.0 / 3.0) + dr - db
ElseIf b = max
*hsl\H = (2.0 / 3.0) + dg - dr
EndIf
If *hsl\H < 0 : *hsl\H + 1 : EndIf
If *hsl\H > 1 : *hsl\H - 1 : EndIf
*hsl\H * 360.0
*hsl\S * 100.0
*hsl\L * 100.0
EndIf
EndProcedure
ProcedureDLL TintImage(Image.l, Color.l)
Protected bpp.b = 0
Protected px.l, pixel.l, newcolor.l
Protected ctr.l
If ImageDepth(Image) < 24
ProcedureReturn
EndIf
GetObject_(ImageID(Image), SizeOf(BITMAP), @bm.BITMAP)
Select bm\bmBitsPixel
Case 24: bpp = 3
Case 32: bpp = 4
EndSelect
GlobalLock_(bm\bmBits)
For i = 0 To bm\bmHeight - 1
px = 0
ctr = 0
While (px < bm\bmWidthBytes) And (ctr < bm\bmWidth)
pixel = bm\bmBits + (i * bm\bmWidthBytes) + px
pixb = (PeekB(pixel + (bpp-1)) & $FF)
pixg = (PeekB(pixel + (bpp-2)) & $FF)
pixr = (PeekB(pixel + (bpp-3)) & $FF)
pixrgb = RGB(pixr, pixg, pixb)
newcolor = _mul(pixrgb, Color)
PokeB(pixel + (bpp - 1), Red(newcolor))
PokeB(pixel + (bpp - 2), Green(newcolor))
PokeB(pixel + (bpp - 3), Blue(newcolor))
px + bpp
ctr + 1
Wend
Next
GlobalUnlock_(bm\bmBits)
EndProcedure
ProcedureDLL TintImageNew(Image.l, Color.l)
Protected bpp.b = 0
Protected px.l, pixel.l, newcolor.l
Protected ctr.l, newimage
GetObject_(ImageID(Image), SizeOf(BITMAP), @bm.BITMAP)
newimage = CreateImage(#PB_Any, bm\bmWidth, bm\bmHeight, bm\bmBitsPixel)
*bits = AllocateMemory(bm\bmHeight * bm\bmWidthBytes)
GetBitmapBits_(ImageID(Image), bm\bmHeight * bm\bmWidthBytes, *bits)
SetBitmapBits_(ImageID(newimage), bm\bmHeight * bm\bmWidthBytes, *bits)
FreeMemory(*bits)
GetObject_(ImageID(newimage), SizeOf(BITMAP), @bm2.BITMAP)
Select bm2\bmBitsPixel
Case 24: bpp = 3
Case 32: bpp = 4
Default: ProcedureReturn
EndSelect
GlobalLock_(bm2\bmBits)
For i = 0 To bm2\bmHeight - 1
px = 0
ctr = 0
While (px < bm2\bmWidthBytes) And (ctr < bm2\bmWidth)
pixel = bm2\bmBits + (i * bm2\bmWidthBytes) + px
pixr = (PeekB(pixel + (bpp-1)) & $FF)
pixg = (PeekB(pixel + (bpp-2)) & $FF)
pixb = (PeekB(pixel + (bpp-3)) & $FF)
pixrgb = RGB(pixr, pixg, pixb)
newcolor = _mul(pixrgb, Color)
PokeB(pixel + (bpp - 1), Red(newcolor))
PokeB(pixel + (bpp - 2), Green(newcolor))
PokeB(pixel + (bpp - 3), Blue(newcolor))
px + bpp
ctr + 1
Wend
Next
GlobalUnlock_(bm2\bmBits)
ProcedureReturn newimage
EndProcedure
Code: Select all
; save this file in the same folder where you saved tint.pbi
IncludeFile "tint.pbi"
Global hsl.HSLColor
hsl\H = 50.0
hsl\S = 50.0
hsl\L = 50.0
Global baseColor.l = HSL2RGB(@hsl)
Global tintColor.l = HSL2RGB(@hsl)
Global tintImage.l
Enumeration
#MainWindow
#ScrollArea
#Image
#Button
#ColorPicker
#TrackBar
#cpImage
#tmpImage
EndEnumeration
Procedure UpdateCPImage()
StartDrawing(ImageOutput(#cpImage))
Box(0, 0, 25, 25, baseColor)
DrawingMode(#PB_2DDrawing_Outlined)
Box(0, 0, 25, 25, #Black)
StopDrawing()
SetGadgetState(#ColorPicker, ImageID(#cpImage))
EndProcedure
Procedure OpenMainWindow()
CreateImage(#cpImage, 25, 25)
If OpenWindow(#MainWindow, 359, 105, 607, 485, "Window_0", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget)
If CreateGadgetList(WindowID(#MainWindow))
ScrollAreaGadget(#ScrollArea, 0, 0, 570, 485, 500, 400, 10, #PB_ScrollArea_Single)
ImageGadget(#Image, 0, 0, 100, 100, 0)
CloseGadgetList()
ButtonGadget(#Button, 575, 5, 25, 20, "...")
ImageGadget(#ColorPicker, 575, 30, 25, 25, ImageID(#cpImage))
TrackBarGadget(#TrackBar, 580, 60, 20, 405, 0, 50, #PB_TrackBar_Ticks|#PB_TrackBar_Vertical)
SetGadgetState(#TrackBar, hsl\L)
UpdateCPImage()
EndIf
EndIf
EndProcedure
UseJPEGImageDecoder()
UsePNGImageDecoder()
OpenMainWindow()
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
Select EventGadget()
Case #Button
pattern$ = "Bitmap (*.bmp)|*.bmp|Jpeg (*.jpg, *.jpeg)|*.jpg;*.jpeg|PNG (*.png)|*.png"
file$ = OpenFileRequester("Select an image", "", pattern$, 0)
If file$ <> ""
LoadImage(#tmpImage, file$)
If IsImage(#tmpImage)
tintImage = TintImageNew(#tmpImage, tintColor)
If IsImage(tintImage)
SetGadgetState(#Image, ImageID(tintImage))
Else
MessageRequester("Error", "Could not tint image")
EndIf
EndIf
EndIf
Case #ColorPicker
If EventType() = #PB_EventType_LeftClick
clr = ColorRequester(baseColor)
If clr >= 0
baseColor = clr
RGB2HSL(clr, @hsl)
hsl\L = 100 - GetGadgetState(#TrackBar)
;hsl\S = GetGadgetState(#TrackBar)
tintColor.l = HSL2RGB(@hsl)
UpdateCPImage()
If IsImage(#tmpImage)
tintImage = TintImageNew(#tmpImage, tintColor)
SetGadgetState(#Image, ImageID(tintImage))
EndIf
EndIf
EndIf
Case #TrackBar
hsl\L = 100 - GetGadgetState(#TrackBar)
;hsl\S = GetGadgetState(#TrackBar)
tintColor = HSL2RGB(@hsl)
If IsImage(#tmpImage)
tintImage = TintImageNew(#tmpImage, tintColor)
SetGadgetState(#Image, ImageID(tintImage))
EndIf
EndSelect
Case #PB_Event_CloseWindow
EventWindow = EventWindow()
If EventWindow = #MainWindow
CloseWindow(#MainWindow)
Break
EndIf
EndSelect
ForEver
That's the basic idea. I hope it's useful to you Seymour; if not maybe others will find some use for it.