How can I use a 2 color svg and override it's palette depending on the background color?

Usually I have 1 color svg-s, like:   that look like:

<?xml version="1.0" encoding="utf-8"?>
<svg width="40" height="40" viewBox="1 1 22 22" fill="none" stroke-width="4" stroke-linecap="square" xmlns="http://www.w3.org/2000/svg">
<path id="3" d="M22 9h-13" stroke="#000000"/>
</svg>

and I include them in my app like:

<drawables>
	<bitmap id="test" filename="test.svg" dithering="none">
		<palette disableTransparency="false">
			<color>000000</color>
		</palette>
	</bitmap>
	<bitmap id="test_dark" filename="test.svg" dithering="none">
		<palette disableTransparency="false">
			<color>FFFFFF</color>
		</palette>
	</bitmap>
</drawables>

and in my code I do something like:

WatchUi.loadResource(getBackgroundColor() == COLOR_BLACK ? Rez.Drawables.test_dark : Rez.Drawables.test);

Now I'd like to have an svg with 2 colors:    sorry, original svg couldn't be uploaded. but it's this:

<?xml version="1.0" encoding="utf-8"?>
<svg width="40px" height="40px" viewBox="1 1 22 22" fill="none" stroke-width="4" stroke-linecap="square" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
<path id="4" d="M22 3h-19" stroke="#AAAAAA"/>
<path id="3" d="M22 9h-13" stroke="#AAAAAA"/>
<path id="2" d="M22 15h-7" stroke="#000000"/>
<path id="1" d="M22 21h-1" stroke="#000000"/>
</svg>

And I'd like to be able to use this similar to the 2 color version above, just with the following change:

<drawables>
	<bitmap id="test" filename="test2.svg" dithering="none">
		<palette disableTransparency="false">
			<color>000000</color>
			<color>AAAAAA</color>
		</palette>
	</bitmap>
	<bitmap id="test_dark" filename="test2.svg" dithering="none">
		<palette disableTransparency="false">
			<color>FFFFFF</color>
			<color>555555</color>
		</palette>
	</bitmap>
</drawables>

Notice, that in the dark I not only added a 2nd color to the palette but I also changed the AAAAAA (that is in the svg) to 555555 (because that's the "inverse" what I need in a dark background)

However this doesn't work, and I hope it should work, just that I am doing something wrong. Did anyone manage to do something like this?

  • Notice, that in the dark I not only added a 2nd color to the palette but I also changed the AAAAAA (that is in the svg) to 555555 (because that's the "inverse" what I need in a dark background)

    Sorry if I'm missing something obvious, I don't understand this comment (specifically the part about adding a 2nd colour to the palette). Don't both palettes have 2 colours? Do you not count 000000 as a "colour"?

    It looks to me from what you posted that the first palette has the colours 000000 and AAAAAA, while the second palette has FFFFFF and 555555.

    EDIT: I guess it makes sense if you are comparing the 2-colour palette to the 1-colour palette, but the 1-colour palette doesn't have the AAAAAA colour, so I still don't get it.

    However this doesn't work, and I hope it should work, just that I am doing something wrong.

    It sounds like you expect that specifying a palette in a bitmap resource definition should cause the existing colours in the bitmap to be automatically remapped, but I don't think that's what bitmap palettes are for (in Monkey C resources). I think that specifying a palette here simply tells the resource compiler: "these are the only colours that my bitmap uses". Why can't the resource compiler figure that out itself, you might ask? I guess theoretically it could in some cases, but perhaps they chose not to implement that functionality.

    My understanding is that you want to remap your bitmap's colours. e.g. something like this:

    000000 => FFFFFF
    AAAAAA => 555555

    I think you should be able to achieve this using a BufferedBitmap, which allows you to get and set the palette of the associated bitmap.

    [https://forums.garmin.com/developer/connect-iq/f/discussion/6697/re-mapping-colours-on-a-bitmap-to-specific-values-using-pallete/44880#44880]

  • In the usual case you use a bitmap that has 1 color. Of course this is never the case, because if that was true then all you would be able to see is a rectangle filled with the 1 color :) So what instead happens if I understand correctly is that the palette in the xml tells the compiler which color to use as color, and get rid of the others in some way. Depending whether you pass the confusingly named disableTransparency parameter and the value it can either mean that the compiler approximates the colors in the image with the colors in the palette, or to leave the rest as transparent. At least this is my understanding of what this is for.

    So for example you could have a 16 color image that you want to display in a 8 color device like fr55, then this would map the greys to either white or black, and the dark red and red to red, the dark blue and blue to blue, etc.

    One more use is when you provide gifs that don't support transparency and by telling the compiler which colors you want to display it'll know which one is to be replaced by transparent pixels.

    What I am trying to 2 is is slightly different (that kind of includes both things): I have a picture with 3 colors: 1.black, 2.light gray and 3.transparent. I'd like to have 2 different bitmaps:
    1. one I'll use when the background is white: 1.black, 2. light gray, 3. transparent (which is basically the original image)
    2. one that I'll use when the background is black: 1.white, 2 dark gray, 3. transparent

    Where I am failing is the 2nd case. I think I know the reason: when the compiler is approximating the light gray colors in the original image with the 2 colors I gave (white and dark gray) then it's closer to the white, so it switches them to withe. What I want is to switch them to dark gray.

    I think that the colors in <palette> are a set and not a list. Changing the order doesn't change anything. What I want would need it to be a list that replaces the indexed colors in the picture to other colors based on their index in the palette I provide. I guess this is not how this works in the compiler, but I was hoping that maybe I am wrong and someone knows how to do it.

  • I think that the colors in <palette> are a set and not a list. Changing the order doesn't change anything. What I want would need it to be a list that replaces the indexed colors in the picture to other colors based on their index in the palette I provide.

    Yeah I realize that. As I said above, that's apparently not how it works. The documentation doesn't really suggest otherwise.

    Imagine if you imported a 24-bit BMP or PNG and you only wanted to specify 3 colours in your palette (which serves to remap 3 original colours to 3 new colours). How would the compiler know which of the 2^24 possible colours in the image should be mapped to each of the colours in your palette?

    In general, you would want a palette (which works as it currently does, to restrict the colours in the original image), and a list of mappings from original colours to new colours, but that doesn't exist in the resource definition.

    As I said, I think you can achieve what you want at runtime using a BufferedBitmap.

  • Interesting! So this is doing exactly what I want, just in runtime, instead of compile time. I'll try this. In fact it might be even beneficial, 'cause I won't need the
    var bitmap = isDarkMode ? Rez.Drawables.foo_dark :Rez.Drawables.foo;
    in the code for EACH icon, the prg will only have 1 drawable instead of 2, and I'll only need this in 1 place:
    var palette = ...;
    palette[1] = isDarkMode ? 0x555555 : 0xAAAAAA;
    and then just draw it with each drawable.

    The only thing I'll need to make sure somehow is that the order of the colors is always the same in the bitmaps....

  • The only thing I'll need to make sure somehow is that the order of the colors is always the same in the bitmaps....

    Not necessarily, as you can always dynamically modify the original palette at runtime (find the position of the actual original colour at runtime as opposed to hardcoding the array index), at the cost of some additional code. You could even write a reusuable function that takes a list of colour mappings and applies the palette modification in a generic way (although this might be a little heavy if you only ever use it to remap 2-colour palettes.)

    However, the linked comment (posted by an ex-Garmin employee) implies that the colours returned by getPalette will match the order of the colours in the resource palette. The comment also implies that the colours in the resource palette are in fact a list.

    You will want to define your bitmap resource with the palette [0xFFFFFF, 0x5555555] (this will implicitly add TRANSPARENT to the end unless you use the disableTransparency attribute)

    In your code, you can load this bitmapResource, and use it to initialize a BufferedBitmap object. Once you have done that you should be able to pass that BufferedBitmap object to dc.drawBitmap in the same way you can with the bitmapResource. If you call getPalette() on the BufferedBitmap object you should get this array back: [0xFFFFFF,0x555555,Graphics.COLOR_TRANSPARENT].