I am trying (mostly successfully) to \"read\" the colors from the active ThemeColorScheme
.
The subroutine below will obtain 12 colors from the theme,
At first sight Floris' solution seems to work, but if you're concerned with accuracy you'll soon realize that the previous solution matches the office color calculations for just a minor part of the color space.
Office seems to use HSL color mode while calculating tinting and shading, and using this technique gives us almost 100% accurate color calculations (tested on Office 2013).
The methodology for calculating the values correctly seems to be:
To find the tint/shade values (step #3), you look at the Luminosity-value of the HSL color and uses this table (found by trial & error):
| [0.0] | <0.0 - 0.2> | [0.2 - 0.8] | <0.8 - 1.0> | [1.0] |
|:-----:|:-----------:|:-----------:|:-----------:|:-----:|
| + .50 | + .90 | + .80 | - .10 | - .05 |
| + .35 | + .75 | + .60 | - .25 | - .15 |
| + .25 | + .50 | + .40 | - .50 | - .25 |
| + .10 | + .25 | - .25 | - .75 | - .35 |
| + .05 | + .10 | - .50 | - .90 | - .50 |
Positive values are tinting the color (making it lighter), and negative values are shading the color (making it darker). There are five groups; 1 group for completely black and 1 group for completely white. These will just match these specific values (and not e.g. RGB = {255, 255, _254_}
). Then there are two small ranges of very dark and very light colors that are treated separately, and finally a big range for all of the rest colors.
Note: A value of +0.40 means that the value will get 40% lighter, not that it is a 40% tint of the original color (which actually means that it is 60% lighter). This might be confusing to someone, but this is the way Office uses these values internally (i.e. in Excel through the TintAndShade
property of the Cell.Interior
).
[Disclaimer]: I've built upon Floris' solution to create this VBA. A lot of the HSL translation code is also copied from a Word article mentioned in the comments already.
The output from the code below is the following color variations:
At first glance, this looks very similar to Floris' solution, but on closer inspection you can clearly see the difference in many situations. Office theme colors (and thus this solution) is generally more saturated the the plain RGB lighten/darken technique.
Option Explicit
Public Type HSL
h As Double ' Range 0 - 1
S As Double ' Range 0 - 1
L As Double ' Range 0 - 1
End Type
Public Type RGB
R As Byte
G As Byte
B As Byte
End Type
Sub CalcColor()
Dim ii As Integer, jj As Integer
Dim pres As Presentation
Dim schemeColors As ThemeColorScheme
Dim ts As Double
Dim c, c2 As Long
Dim hc As HSL, hc2 As HSL
Set pres = ActivePresentation
Set schemeColors = pres.Designs(1).SlideMaster.Theme.ThemeColorScheme
' For all colors
For ii = 0 To 11
c = schemeColors(ii + 1).RGB
' Generate all the color variations
For jj = 0 To 5
hc = RGBtoHSL(c)
ts = SelectTintOrShade(hc, jj)
hc2 = ApplyTintAndShade(hc, ts)
c2 = HSLtoRGB(hc2)
Call CreateShape(pres.Slides(1), ii, jj, c2)
Next jj
Next ii
End Sub
' The tint and shade value is a value between -1.0 and 1.0, where
' -1.0 means fully shading (black), and 1.0 means fully tinting (white)
' A tint/shade value of 0.0 will not change the color
Public Function SelectTintOrShade(hc As HSL, variationIndex As Integer) As Double
Dim shades(5) As Variant
shades(0) = Array(0#, 0.5, 0.35, 0.25, 0.15, 0.05)
shades(1) = Array(0#, 0.9, 0.75, 0.5, 0.25, 0.1)
shades(2) = Array(0#, 0.8, 0.6, 0.4, -0.25, -0.5)
shades(3) = Array(0#, -0.1, -0.25, -0.5, -0.75, -0.9)
shades(4) = Array(0#, -0.05, -0.15, -0.25, -0.35, -0.5)
Select Case hc.L
Case Is < 0.001: SelectTintOrShade = shades(0)(variationIndex)
Case Is < 0.2: SelectTintOrShade = shades(1)(variationIndex)
Case Is < 0.8: SelectTintOrShade = shades(2)(variationIndex)
Case Is < 0.999: SelectTintOrShade = shades(3)(variationIndex)
Case Else: SelectTintOrShade = shades(4)(variationIndex)
End Select
End Function
Public Function ApplyTintAndShade(hc As HSL, TintAndShade As Double) As HSL
If TintAndShade > 0 Then
hc.L = hc.L + (1 - hc.L) * TintAndShade
Else
hc.L = hc.L + hc.L * TintAndShade
End If
ApplyTintAndShade = hc
End Function
Sub CreateShape(slide As slide, xIndex As Integer, yIndex As Integer, color As Long)
Dim newShape As Shape
Dim xStart As Integer, yStart As Integer
Dim xOffset As Integer, yOffset As Integer
Dim xSize As Integer, ySize As Integer
xStart = 100
yStart = 100
xOffset = 30
yOffset = 30
xSize = 25
ySize = 25
Set newShape = slide.Shapes.AddShape(msoShapeRectangle, xStart + xOffset * xIndex, yStart + yOffset * yIndex, xSize, ySize)
newShape.Fill.BackColor.RGB = color
newShape.Fill.ForeColor.RGB = color
newShape.Line.ForeColor.RGB = 0
newShape.Line.BackColor.RGB = 0
End Sub
' From RGB to HSL
Function RGBtoHSL(ByVal RGB As Long) As HSL
Dim R As Double ' Range 0 - 1
Dim G As Double ' Range 0 - 1
Dim B As Double ' Range 0 - 1
Dim RGB_Max As Double
Dim RGB_Min As Double
Dim RGB_Diff As Double
Dim HexString As String
HexString = Right$(String$(7, "0") & Hex$(RGB), 8)
R = CDbl("&H" & Mid$(HexString, 7, 2)) / 255
G = CDbl("&H" & Mid$(HexString, 5, 2)) / 255
B = CDbl("&H" & Mid$(HexString, 3, 2)) / 255
RGB_Max = R
If G > RGB_Max Then RGB_Max = G
If B > RGB_Max Then RGB_Max = B
RGB_Min = R
If G < RGB_Min Then RGB_Min = G
If B < RGB_Min Then RGB_Min = B
RGB_Diff = RGB_Max - RGB_Min
With RGBtoHSL
.L = (RGB_Max + RGB_Min) / 2
If RGB_Diff = 0 Then
.S = 0
.h = 0
Else
Select Case RGB_Max
Case R: .h = (1 / 6) * (G - B) / RGB_Diff - (B > G)
Case G: .h = (1 / 6) * (B - R) / RGB_Diff + (1 / 3)
Case B: .h = (1 / 6) * (R - G) / RGB_Diff + (2 / 3)
End Select
Select Case .L
Case Is < 0.5: .S = RGB_Diff / (2 * .L)
Case Else: .S = RGB_Diff / (2 - (2 * .L))
End Select
End If
End With
End Function
' .. and back again
Function HSLtoRGB(ByRef HSL As HSL) As Long
Dim R As Double
Dim G As Double
Dim B As Double
Dim X As Double
Dim Y As Double
With HSL
If .S = 0 Then
R = .L
G = .L
B = .L
Else
Select Case .L
Case Is < 0.5: X = .L * (1 + .S)
Case Else: X = .L + .S - (.L * .S)
End Select
Y = 2 * .L - X
R = H2C(X, Y, IIf(.h > 2 / 3, .h - 2 / 3, .h + 1 / 3))
G = H2C(X, Y, .h)
B = H2C(X, Y, IIf(.h < 1 / 3, .h + 2 / 3, .h - 1 / 3))
End If
End With
HSLtoRGB = CLng("&H00" & _
Right$("0" & Hex$(Round(B * 255)), 2) & _
Right$("0" & Hex$(Round(G * 255)), 2) & _
Right$("0" & Hex$(Round(R * 255)), 2))
End Function
Function H2C(X As Double, Y As Double, hc As Double) As Double
Select Case hc
Case Is < 1 / 6: H2C = Y + ((X - Y) * 6 * hc)
Case Is < 1 / 2: H2C = X
Case Is < 2 / 3: H2C = Y + ((X - Y) * ((2 / 3) - hc) * 6)
Case Else: H2C = Y
End Select
End Function
Based on the above solution with HSL values adding here a demo that works in Excel. Works in conjunction with the above listed HSL solution.
Sub DemoExcelThemecolorsHSL()
Dim rng As Range
Dim n As Integer, m As Integer
Dim arrNames
Dim arrDescriptions
Dim arrValues
Dim schemeColors As ThemeColorScheme
Dim dblTintShade As Double
Dim lngColorRGB As Long, lngColorRGBshaded As Long
Dim ColorHSL As HSL, ColorHSLshaded As HSL
Set schemeColors = ActiveWorkbook.Theme.ThemeColorScheme
arrNames = Array("xlThemeColorDark1", "xlThemeColorLight1", "xlThemeColorDark2", "xlThemeColorLight2", "xlThemeColorAccent1", "xlThemeColorAccent2", _
"xlThemeColorAccent3", "xlThemeColorAccent4", "xlThemeColorAccent5", "xlThemeColorAccent6", "xlThemeColorHyperlink", "xlThemeColorFollowedHyperlink")
arrDescriptions = Array("Dark1", "Light1", "Dark2", "Light2", "Accent1", "Accent2", "Accent3", "Accent4", "Accent5", "Accent6", "Hyperlink", "Followed hyperlink")
arrValues = Array(2, 1, 4, 3, 5, 6, 7, 8, 9, 10, 11, 12)
' New sheet, title row
ActiveWorkbook.Worksheets.Add
Set rng = Cells(1, 2)
rng(1, 1).Value2 = "ThemeColor Name"
rng(1, 2).Value2 = "Value"
rng(1, 3).Value2 = "Description"
rng(1, 4).Value2 = "TintAndShade"
rng.Resize(1, 4).Font.Bold = True
Set rng = rng(3, 1)
' color matrix
For n = 0 To 11
rng(n * 2, 1).Value = arrNames(n)
rng(n * 2, 2).Value = arrValues(n)
rng(n * 2, 3).Value = arrDescriptions(n)
lngColorRGB = schemeColors(n + 1).RGB
For m = 0 To 5
ColorHSL = RGBtoHSL(lngColorRGB)
dblTintShade = SelectTintOrShade(ColorHSL, m)
ColorHSLshaded = ApplyTintAndShade(ColorHSL, dblTintShade)
lngColorRGBshaded = HSLtoRGB(ColorHSLshaded)
With rng(n * 2, m + 4)
.Value = dblTintShade
If ColorHSLshaded.L < 0.5 Then .Font.ColorIndex = 2
' fixed color, not changing when a new Color scheme is being selected
.Interior.color = lngColorRGBshaded
' cell color dependent on selected color palette
.Offset(1, 0).Interior.ThemeColor = arrValues(n)
.Offset(1, 0).Interior.TintAndShade = dblTintShade
End With
Next m
Next n
rng.Resize(1, 3).EntireColumn.AutoFit
End Sub
If you use VBA for excel, you can record your keystrokes. Selecting another color (from below the theme) shows:
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorLight2
.TintAndShade = 0.599993896298105
.PatternTintAndShade = 0
The .TintAndShade
factor modifies the defined color. Different colors in the theme use different values for .TintAndShade
- sometimes the numbers are negative (to make light colors darker).
Incomplete table of .TintAndShade
(for the theme I happened to have in Excel, first two colors):
0.00 0.00
-0.05 0.50
-0.15 0.35
-0.25 0.25
-0.35 0.15
-0.50 0.05
EDIT some code that "more or less" does the conversion - you need to make sure that you have the right values in your shades
, but otherwise the conversion of colors seems to work
updated to be pure PowerPoint code, with output shown at the end
Option Explicit
Sub calcColor()
Dim ii As Integer, jj As Integer
Dim pres As Presentation
Dim thm As OfficeTheme
Dim themeColor As themeColor
Dim schemeColors As ThemeColorScheme
Dim shade
Dim shades(12) As Variant
Dim c, c2 As Long
Dim newShape As Shape
Set pres = ActivePresentation
Set schemeColors = pres.Designs(1).SlideMaster.Theme.ThemeColorScheme
shades(0) = Array(0, -0.05, -0.15, -0.25, -0.35, -0.5)
shades(1) = Array(0, 0.05, 0.15, 0.25, 0.35, 0.5)
shades(2) = Array(-0.1, -0.25, -0.5, -0.75, -0.9)
For ii = 3 To 11
shades(ii) = Array(-0.8, -0.6, -0.4, 0.25, 0.5)
Next
For ii = 0 To 11
c = schemeColors(ii + 1).RGB
For jj = 0 To 4
c2 = fadeRGB(c, shades(ii)(jj))
Set newShape = pres.Slides(1).Shapes.AddShape(msoShapeRectangle, 200 + 30 * ii, 200 + 30 * jj, 25, 25)
newShape.Fill.BackColor.RGB = c2
newShape.Fill.ForeColor.RGB = c2
newShape.Line.ForeColor.RGB = 0
newShape.Line.BackColor.RGB = 0
Next jj
Next ii
End Sub
Function fadeRGB(ByVal c, s) As Long
Dim r, ii
r = toRGB(c)
For ii = 0 To 2
If s < 0 Then
r(ii) = Int((r(ii) - 255) * s + r(ii))
Else
r(ii) = Int(r(ii) * (1 - s))
End If
Next ii
fadeRGB = r(0) + 256& * (r(1) + 256& * r(2))
End Function
Function toRGB(c)
Dim retval(3), ii
For ii = 0 To 2
retval(ii) = c Mod 256
c = (c - retval(ii)) / 256
Next
toRGB = retval
End Function