diff --git a/bin/data/cmclient-5.grf b/bin/data/cmclient-5.grf index 059f98f9..0f95982f 100644 Binary files a/bin/data/cmclient-5.grf and b/bin/data/cmclient-5.grf differ diff --git a/grf/cmclient/gencmclientgrf.py b/grf/cmclient/gencmclientgrf.py index b7b04f78..14cd3ebc 100644 --- a/grf/cmclient/gencmclientgrf.py +++ b/grf/cmclient/gencmclientgrf.py @@ -1,18 +1,24 @@ import grf -import spectra -gen = grf.NewGRF( - b'CMC\x01', - 'CityMania Client Resourse Pack v5', - 'Provides additional resources for a CityMania patched client (citymania.org/downloads). Should be put in your client data folder. Do not add this to your game via NewGRF options, it may break something.', -) +import numpy as np + + +g = grf.BaseNewGRF() +g.add(grf.SetDescription( + format_version=8, + grfid=b'CMC\x01', + name='CityMania Client Resourse Pack v5', + description='Provides additional resources for a CityMania patched client (citymania.org/downloads). Should be put in your client data folder. Do not add this to your game via NewGRF options, it may break something.', +)) toolbar_png = grf.ImageFile('sprites/toolbar.png') -gen.add_sprite(grf.FileSprite(toolbar_png, 0, 0, 20, 14, yofs=4), - grf.FileSprite(toolbar_png, 32, 0, 40, 40, zoom=grf.ZOOM_2X)) +g.add(grf.AlternativeSprites( + grf.FileSprite(toolbar_png, 0, 0, 20, 14, yofs=4), + grf.FileSprite(toolbar_png, 32, 0, 40, 40, zoom=grf.ZOOM_2X) +)) -sprite = lambda *args, **kw: gen.add_sprite(grf.FileSprite(toolbar_png, *args, **kw)) -sprite2 = lambda x, y, w, h, x2, y2: gen.add_sprite(grf.FileSprite(toolbar_png, x, y, w, h), grf.FileSprite(toolbar_png, x2, y2, w * 2, h * 2, zoom=grf.ZOOM_2X)) +sprite = lambda *args, **kw: g.add(grf.FileSprite(toolbar_png, *args, **kw)) +sprite2 = lambda x, y, w, h, x2, y2: g.add(grf.AlternativeSprites(grf.FileSprite(toolbar_png, x, y, w, h), grf.FileSprite(toolbar_png, x2, y2, w * 2, h * 2, zoom=grf.ZOOM_2X))) sprite( 0, 44, 12, 10) # hq button icon sprite( 0, 55, 12, 10) # watch button icon # sprite2(0, 55, 12, 10, 13, 55) # watch button icon @@ -31,7 +37,7 @@ innerhl_png = grf.ImageFile('sprites/innerhighlight00.png') -sprite = lambda *args, **kw: gen.add_sprite(grf.FileSprite(innerhl_png, *args, **kw)) +sprite = lambda *args, **kw: g.add(grf.FileSprite(innerhl_png, *args, **kw)) sprite( 18, 8, 64, 31, xofs=-31, yofs= 7) sprite( 98, 8, 64, 31, xofs=-31, yofs= 7) sprite(178, 8, 64, 23, xofs=-31, yofs= 7) @@ -56,7 +62,7 @@ sprite(185, 125, 18, 15, xofs= -8, yofs= 7) #red -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x01, 0x09, 0x00], [0x0A, 0x0B, 0xB6], [0x0C, 0x0D, 0xB7], @@ -65,49 +71,49 @@ ])) # green -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x0A, 0x0B, 0xCF], [0x0C, 0x0D, 0xD0], [0x0E, 0x0F, 0xD1], ])) # black -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x0A, 0x0B, 0x10], [0x0C, 0x0D, 0x11], [0x0E, 0x0F, 0x12], ])) # ligth blue -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x0A, 0x0B, 0x96], [0x0C, 0x0D, 0x97], [0x0E, 0x0F, 0x98], ])) # orange -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x0A, 0x0B, 0xB9], [0x0C, 0x0D, 0xBA], [0x0E, 0x0F, 0xBB], ])) # white -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x0A, 0x0B, 0x0D], [0x0C, 0x0D, 0x0E], [0x0E, 0x0F, 0x0F], ])) # white (yellow) -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x0A, 0x0B, 0x32], [0x0C, 0x0D, 0x33], [0x0E, 0x0F, 0x34], ])) # white (purple) -gen.add_sprite(grf.PaletteRemap([ +g.add(grf.PaletteRemap([ [0x0A, 0x0B, 0xAC], [0x0C, 0x0D, 0xAD], [0x0E, 0x0F, 0xAE], @@ -141,47 +147,54 @@ x = SPRITE_MARGIN _, _, w, h, xofs, yofs = p for _ in range(1, 16 + 4): - gen.add_sprite(grf.FileSprite(borderhl_png, x, y, w, h, xofs=xofs, yofs=yofs)) + g.add(grf.FileSprite(borderhl_png, x, y, w, h, xofs=xofs, yofs=yofs)) x += SPRITE_MARGIN + w y += h + SPRITE_MARGIN def gen_tint(tint, ratio): - return lambda x: x.blend(tint, ratio=ratio) + tint = grf.srgb_to_oklab(np.array(tint) * 255) + return lambda x: grf.oklab_blend(x, tint, ratio=1.-(1 - ratio)**2) + def gen_brightness(level): def func(x): if level > 0: - return x.brighten(amount=2.56 * level) + return grf.oklab_blend(x, grf.srgb_to_oklab((255, 255, 192)), ratio=0.04 * level) else: - return x.darken(amount=-2.56 * level) + return grf.oklab_blend(x, grf.srgb_to_oklab((4, 0, 16)), ratio=-0.04 * level) + # l, a, b = x + # l = min(max(l + 0.019 * level, 0.), 1.) + # return np.array((l, a, b)) return func + def gen_white_tint_contrast(): def func(x): - white = spectra.rgb(1.0, 1.0, 1.0) - x = x.blend(white, ratio=0.4) - if grf.color_distance(x, white) < 0.5: - x = x.blend(spectra.rgb(0.5, 1.0, 0.0), ratio=0.2) + # white = grf.srgb_to_oklab((255, 255, 255)) + white = np.array((1., 0, 0)) + x = grf.oklab_blend(x, white, ratio=0.4) + # if grf.color_distance(x, white) < 0.5: + # x = x.blend(spectra.rgb(0.5, 1.0, 0.0), ratio=0.2) return x return func -remap = lambda f: gen.add_sprite(grf.PaletteRemap.from_function(f, remap_water=True)) -remap(gen_tint(spectra.rgb(1, 0, 0), 0.6)) # deep red tint -remap(gen_tint(spectra.rgb(1, 0.5, 0), 0.65)) # deep orange tint -remap(gen_tint(spectra.rgb(0, 1, 0), 0.65)) # deep green tint -remap(gen_tint(spectra.rgb(0, 1, 1), 0.65)) # deep cyan tint -remap(gen_tint(spectra.rgb(1, 0, 0), 0.4)) # red tint -remap(gen_tint(spectra.rgb(1, 0.5, 0), 0.4)) # orange tint -remap(gen_tint(spectra.rgb(1.0, 1.0, 0), 0.4)) # yellow tint -remap(gen_tint(spectra.rgb(1.0, 1.0, 0.5), 0.4)) # yellow white tint +remap = lambda f: g.add(grf.PaletteRemap.oklab_from_function(f, remap_water=True)) +remap(gen_tint((1, 0, 0), 0.6)) # deep red tint +remap(gen_tint((1, 0.5, 0), 0.65)) # deep orange tint +remap(gen_tint((0, 1, 0), 0.65)) # deep green tint +remap(gen_tint((0, 1, 1), 0.65)) # deep cyan tint +remap(gen_tint((1, 0, 0), 0.4)) # red tint +remap(gen_tint((1, 0.5, 0), 0.4)) # orange tint +remap(gen_tint((1.0, 1.0, 0), 0.4)) # yellow tint +remap(gen_tint((1.0, 1.0, 0.5), 0.4)) # yellow white tint remap(gen_white_tint_contrast()) # white tint -remap(gen_tint(spectra.rgb(0, 1.0, 0), 0.4)) # green tint -remap(gen_tint(spectra.rgb(0, 1.0, 1.0), 0.4)) # cyan tint -remap(gen_tint(spectra.rgb(0.5, 1.0, 1.0), 0.4)) # cyan white tint -remap(gen_tint(spectra.rgb(0, 0, 1.0), 0.4)) # blue tint +remap(gen_tint((0, 1.0, 0), 0.4)) # green tint +remap(gen_tint((0, 1.0, 1.0), 0.4)) # cyan tint +remap(gen_tint((0.5, 1.0, 1.0), 0.4)) # cyan white tint +remap(gen_tint((0, 0, 1.0), 0.4)) # blue tint B = 22.2 remap(gen_brightness(21.7 - B)) # shade N @@ -193,4 +206,9 @@ def func(x): remap(gen_brightness(17.1 - B)) # shade W remap(gen_brightness(17.5 - B)) # shade NW -gen.write('../../bin/data/cmclient-5.grf') +grf.main(g, '../../bin/data/cmclient-5.grf') + +# func = gen_brightness(17.1 - B) +# grf.make_palette_image([grf.oklab_to_srgb(func(x)) for x in grf.OKLAB_PALETTE]).show() +# remap = grf.PaletteRemap.oklab_from_function(func, remap_water=True).remap +# grf.make_palette_image([grf.PALETTE[x] for x in remap]).show() diff --git a/grf/cmclient/grf.py b/grf/cmclient/grf.py deleted file mode 100644 index ec6e198b..00000000 --- a/grf/cmclient/grf.py +++ /dev/null @@ -1,290 +0,0 @@ -import math - -from PIL import Image, ImageDraw -from nml.spriteencoder import SpriteEncoder -import spectra -import struct -import numpy as np - -to_spectra = lambda r, g, b: spectra.rgb(float(r) / 255., float(g) / 255., float(b) / 255.) -# working with DOS palette only -PALETTE = (0, 0, 255, 16, 16, 16, 32, 32, 32, 48, 48, 48, 64, 64, 64, 80, 80, 80, 100, 100, 100, 116, 116, 116, 132, 132, 132, 148, 148, 148, 168, 168, 168, 184, 184, 184, 200, 200, 200, 216, 216, 216, 232, 232, 232, 252, 252, 252, 52, 60, 72, 68, 76, 92, 88, 96, 112, 108, 116, 132, 132, 140, 152, 156, 160, 172, 176, 184, 196, 204, 208, 220, 48, 44, 4, 64, 60, 12, 80, 76, 20, 96, 92, 28, 120, 120, 64, 148, 148, 100, 176, 176, 132, 204, 204, 168, 72, 44, 4, 88, 60, 20, 104, 80, 44, 124, 104, 72, 152, 132, 92, 184, 160, 120, 212, 188, 148, 244, 220, 176, 64, 0, 4, 88, 4, 16, 112, 16, 32, 136, 32, 52, 160, 56, 76, 188, 84, 108, 204, 104, 124, 220, 132, 144, 236, 156, 164, 252, 188, 192, 252, 208, 0, 252, 232, 60, 252, 252, 128, 76, 40, 0, 96, 60, 8, 116, 88, 28, 136, 116, 56, 156, 136, 80, 176, 156, 108, 196, 180, 136, 68, 24, 0, 96, 44, 4, 128, 68, 8, 156, 96, 16, 184, 120, 24, 212, 156, 32, 232, 184, 16, 252, 212, 0, 252, 248, 128, 252, 252, 192, 32, 4, 0, 64, 20, 8, 84, 28, 16, 108, 44, 28, 128, 56, 40, 148, 72, 56, 168, 92, 76, 184, 108, 88, 196, 128, 108, 212, 148, 128, 8, 52, 0, 16, 64, 0, 32, 80, 4, 48, 96, 4, 64, 112, 12, 84, 132, 20, 104, 148, 28, 128, 168, 44, 28, 52, 24, 44, 68, 32, 60, 88, 48, 80, 104, 60, 104, 124, 76, 128, 148, 92, 152, 176, 108, 180, 204, 124, 16, 52, 24, 32, 72, 44, 56, 96, 72, 76, 116, 88, 96, 136, 108, 120, 164, 136, 152, 192, 168, 184, 220, 200, 32, 24, 0, 56, 28, 0, 72, 40, 4, 88, 52, 12, 104, 64, 24, 124, 84, 44, 140, 108, 64, 160, 128, 88, 76, 40, 16, 96, 52, 24, 116, 68, 40, 136, 84, 56, 164, 96, 64, 184, 112, 80, 204, 128, 96, 212, 148, 112, 224, 168, 128, 236, 188, 148, 80, 28, 4, 100, 40, 20, 120, 56, 40, 140, 76, 64, 160, 100, 96, 184, 136, 136, 36, 40, 68, 48, 52, 84, 64, 64, 100, 80, 80, 116, 100, 100, 136, 132, 132, 164, 172, 172, 192, 212, 212, 224, 40, 20, 112, 64, 44, 144, 88, 64, 172, 104, 76, 196, 120, 88, 224, 140, 104, 252, 160, 136, 252, 188, 168, 252, 0, 24, 108, 0, 36, 132, 0, 52, 160, 0, 72, 184, 0, 96, 212, 24, 120, 220, 56, 144, 232, 88, 168, 240, 128, 196, 252, 188, 224, 252, 16, 64, 96, 24, 80, 108, 40, 96, 120, 52, 112, 132, 80, 140, 160, 116, 172, 192, 156, 204, 220, 204, 240, 252, 172, 52, 52, 212, 52, 52, 252, 52, 52, 252, 100, 88, 252, 144, 124, 252, 184, 160, 252, 216, 200, 252, 244, 236, 72, 20, 112, 92, 44, 140, 112, 68, 168, 140, 100, 196, 168, 136, 224, 200, 176, 248, 208, 184, 255, 232, 208, 252, 60, 0, 0, 92, 0, 0, 128, 0, 0, 160, 0, 0, 196, 0, 0, 224, 0, 0, 252, 0, 0, 252, 80, 0, 252, 108, 0, 252, 136, 0, 252, 164, 0, 252, 192, 0, 252, 220, 0, 252, 252, 0, 204, 136, 8, 228, 144, 4, 252, 156, 0, 252, 176, 48, 252, 196, 100, 252, 216, 152, 8, 24, 88, 12, 36, 104, 20, 52, 124, 28, 68, 140, 40, 92, 164, 56, 120, 188, 72, 152, 216, 100, 172, 224, 92, 156, 52, 108, 176, 64, 124, 200, 76, 144, 224, 92, 224, 244, 252, 200, 236, 248, 180, 220, 236, 132, 188, 216, 88, 152, 172, 244, 0, 244, 245, 0, 245, 246, 0, 246, 247, 0, 247, 248, 0, 248, 249, 0, 249, 250, 0, 250, 251, 0, 251, 252, 0, 252, 253, 0, 253, 254, 0, 254, 255, 0, 255, 76, 24, 8, 108, 44, 24, 144, 72, 52, 176, 108, 84, 210, 146, 126, 252, 60, 0, 252, 84, 0, 252, 104, 0, 252, 124, 0, 252, 148, 0, 252, 172, 0, 252, 196, 0, 64, 0, 0, 255, 0, 0, 48, 48, 0, 64, 64, 0, 80, 80, 0, 255, 255, 0, 32, 68, 112, 36, 72, 116, 40, 76, 120, 44, 80, 124, 48, 84, 128, 72, 100, 144, 100, 132, 168, 216, 244, 252, 96, 128, 164, 68, 96, 140, 255, 255, 255) -SAFE_COLORS = set(range(1, 0xD7)) -ALL_COLORS = set(range(256)) -SPECTRA_PALETTE = {i:to_spectra(PALETTE[i * 3], PALETTE[i * 3 + 1], PALETTE[i * 3 + 2]) for i in range(256)} -WATER_COLORS = set(range(0xF5, 0xFF)) - -# ZOOM_OUT_4X, ZOOM_NORMAL, ZOOM_OUT_2X, ZOOM_OUT_8X, ZOOM_OUT_16X, ZOOM_OUT_32X = range(6) -ZOOM_4X, ZOOM_NORMAL, ZOOM_2X, ZOOM_8X, ZOOM_16X, ZOOM_32X = range(6) -BPP_8, BPP_32 = range(2) - -def color_distance(c1, c2): - rmean = (c1.rgb[0] + c2.rgb[0]) / 2. - r = c1.rgb[0] - c2.rgb[0] - g = c1.rgb[1] - c2.rgb[1] - b = c1.rgb[2] - c2.rgb[2] - return math.sqrt( - ((2 + rmean) * r * r) + - 4 * g * g + - (3 - rmean) * b * b) - - -def find_best_color(x, in_range=SAFE_COLORS): - mj, md = 0, 1e100 - for j in in_range: - c = SPECTRA_PALETTE[j] - d = color_distance(x, c) - if d < md: - mj, md = j, d - return mj - - -# def map_rgb_image(self, im): -# assert im.mode == 'RGB', im.mode -# data = np.array(im) - - -class BaseSprite: - def get_data(self): - raise NotImplemented - - def get_data_size(self): - raise NotImplemented - - -class PaletteRemap(BaseSprite): - def __init__(self, ranges=None): - self.remap = np.arange(256, dtype=np.uint8) - if ranges: - self.set_ranges(ranges) - - def get_data(self): - return b'\x00' + self.remap.tobytes() - - def get_data_size(self): - return 257 - - @classmethod - def from_function(cls, color_func, remap_water=False): - res = cls() - for i in SAFE_COLORS: - res.remap[i] = find_best_color(color_func(SPECTRA_PALETTE[i])) - if remap_water: - for i in WATER_COLORS: - res.remap[i] = find_best_color(color_func(SPECTRA_PALETTE[i])) - return res - - def set_ranges(self, ranges): - for r in ranges: - f, t, v = r - self.remap[f: t + 1] = v - - def remap_image(self, im): - assert im.mode == 'P', im.mode - data = np.array(im) - data = self.remap[data] - res = Image.fromarray(data) - res.putpalette(PALETTE) - return res - - -class RealSprite(BaseSprite): - def __init__(self, w, h, *, xofs=0, yofs=0, zoom=ZOOM_4X): - self.sprite_id = None - self.w = w - self.h = h - # self.file = None - # self.x = None - # self.y = None - self.xofs = xofs - self.yofs = yofs - self.zoom = zoom - - def get_data_size(self): - return 4 - - def get_data(self): - return struct.pack(' 0) - if isinstance(sprites[0], RealSprite): - assert(all(isinstance(s, RealSprite) for s in sprites)) - assert(len(set(s.zoom for s in sprites)) == len(sprites)) - for s in sprites: - s.sprite_id = self._next_sprite_id - self._next_sprite_id += 1 - - for s in sprites: - self.sprites.append(s) - - self.pseudo_sprites.append(sprites[0]) - else: - assert(len(sprites) == 1) - self.pseudo_sprites.append(sprites[0]) - - def _write_pseudo_sprite(self, f, data, grf_type=0xff): - f.write(struct.pack(' recoloursprites() (257 each) - self._write_pseudo_sprite(f, b'\x02\x00\x00\x00') - - for s in self.pseudo_sprites: - self._write_pseudo_sprite(f, s.get_data(), grf_type=0xfd if isinstance(s, RealSprite) else 0xff) - f.write(b'\x00\x00\x00\x00') - for s in self.sprites: - f.write(s.get_real_data()) - - f.write(b'\x00\x00\x00\x00') \ No newline at end of file