How to get the RGB/Long values from PowerPoint color palette

后端 未结 3 1027
礼貌的吻别
礼貌的吻别 2021-01-19 15:18

I am trying (mostly successfully) to \"read\" the colors from the active ThemeColorScheme.

The subroutine below will obtain 12 colors from the theme,

3条回答
  •  孤城傲影
    2021-01-19 16:09

    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.

    A Proper solution - Using HSL 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:

    1. Convert the base RGB color to HSL
    2. Find the tint and shade values to use for the five sub-colors
    3. Apply tint/shade values
    4. Convert back from HSL to RGB color space

    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).

    PowerPoint VBA code to implement the solution

    [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:

    Program output, calculated 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.

    Comparison of the different solutions. This matches office very well!

    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
    

提交回复
热议问题