From 17ad4d4be42ac090cb8ff0ed2e1a549fe1815607 Mon Sep 17 00:00:00 2001 From: Erik Corry Date: Thu, 29 Feb 2024 14:54:52 +0100 Subject: [PATCH] Color transform pngs (#92) * Allow --text on Labels instead of --label. * feedback * Add ability to color-transform PNG images * feedback * feedback --- src/png.toit | 88 +++++++++++++++++- tests/bitmap-2-visualized.toit | 8 +- tests/gold/bitmap-2-visualized.toit.png | Bin 7622 -> 11507 bytes .../three-color-bitmap-2-visualized.toit.png | Bin 1073 -> 1403 bytes tests/png-visualizer.toit | 7 ++ tests/three-color-bitmap-2-visualized.toit | 6 +- tests/toit-png-tools | 2 +- 7 files changed, 107 insertions(+), 4 deletions(-) diff --git a/src/png.toit b/src/png.toit index 3921827..d0f4dcb 100644 --- a/src/png.toit +++ b/src/png.toit @@ -36,6 +36,12 @@ Only PNGs with up to 8 bits per pixel are supported. They can be */ class Png extends CustomElement: png_/png-reader.AbstractPng + last-palette_/ByteArray? := null + last-alpha-palette_/ByteArray? := null + last-transformed-palette_/ByteArray? := null + last-transformed-alpha-palette_/ByteArray? := null + palette-transformer_/PaletteTransformer? := null + palette-transformer-buffer_/ByteArray? := null /** Constructs an element that displays a PNG image, given a byte array @@ -51,7 +57,10 @@ class Png extends CustomElement: --id/string?=null --background=null --border/Border?=null - --png-file/ByteArray: + --png-file/ByteArray + --color/int?=null + --palette-transformer/PaletteTransformer?=null: + palette-transformer_ = palette-transformer info := png-reader.PngInfo png-file if info.uncompressed-random-access: png_ = png-reader.PngRandomAccess png-file @@ -69,6 +78,46 @@ class Png extends CustomElement: --id = id --background = background --border = border + if color: this.color = color + + /** + Causes the PNG to be redrawn with new values from the palette transformer. + */ + invalidate-palette-transformer -> none: + invalidate + last-transformed-palette_ = null + last-transformed-alpha-palette_ = null + + /** + Causes the palette of the PNG to be ignored, and all pixels will + be drawn with the given color. Alpha (transparency) will be + unchanged. + */ + color= value/int -> none: + invalidate-palette-transformer + palette-transformer_ = SingleColorPaletteTransformer_ value + + set-attribute_ key/string value -> none: + if key == "color": + color = value + else: + super key value + /** + The $palette-transformer is an optional PaletteTransformer that transforms + the colors in the PNG. This can be used for example if you have a PNG + that is black-and-transparent, and you want to display it as an + image that is red-and-transparent. + The palette transformer is not called eagerly, so if it is + going to return new values (eg. to change the color of the PNG) + you must call $invalidate-palette-transformer. + A simpler way to use palette transformation is to simply set the + color on this element. This will cause all PNG pixels to be + drawn with the same color, but alpha (transparency) will still + be taken from the PNG file. + */ + palette-transformer= value/PaletteTransformer?: + invalidate-palette-transformer + palette-transformer_ = value // Redraw routine. custom-draw canvas/Canvas: @@ -77,6 +126,26 @@ class Png extends CustomElement: png_.get-indexed-image-data y2 h --accept-8-bit=canvas.supports-8-bit --need-gray-palette=canvas.gray-scale: | y-from/int y-to/int bits-per-pixel/int pixels/ByteArray line-stride/int palette/ByteArray alpha-palette/ByteArray | + if palette-transformer_: + if palette != last-palette_ or alpha-palette != last-alpha-palette_: + if last-transformed-alpha-palette_ == null or + last-transformed-palette_.size != palette.size: + last-transformed-palette_ = ByteArray palette.size + if last-transformed-alpha-palette_ == null or + last-transformed-alpha-palette_.size != palette.size: + last-transformed-alpha-palette_ = ByteArray palette.size + if palette-transformer-buffer_ == null: + palette-transformer-buffer_ = ByteArray 4 + (palette.size / 3).repeat: | i | + 3.repeat: + palette-transformer-buffer_[it] = palette[i * 3 + it] + palette-transformer-buffer_[3] = alpha-palette[i] + palette-transformer_.transform palette-transformer-buffer_ + 3.repeat: + last-transformed-palette_[i * 3 + it] = palette-transformer-buffer_[it] + last-transformed-alpha-palette_[i] = palette-transformer-buffer_[3] + palette = last-transformed-palette_ + alpha-palette = last-transformed-alpha-palette_ if bits-per-pixel == 1: // Last line a little shorter because it has no stride padding. adjust := line-stride - ((round-up w 8) >> 3) @@ -98,3 +167,20 @@ class Png extends CustomElement: y2 = y-to type -> string: return "png" + +interface PaletteTransformer: + /** + Takes a 4-element byte array in rgba order and modifies the + byte array to indicate which color and transparency should + be used instead. + */ + transform rgba/ByteArray -> none + +class SingleColorPaletteTransformer_ implements PaletteTransformer: + color_/int + constructor .color_: + + transform rgba/ByteArray -> none: + rgba[0] = color_ >> 16 + rgba[1] = color_ >> 8 + rgba[2] = color_ diff --git a/tests/bitmap-2-visualized.toit b/tests/bitmap-2-visualized.toit index 3093005..71967c5 100644 --- a/tests/bitmap-2-visualized.toit +++ b/tests/bitmap-2-visualized.toit @@ -22,7 +22,7 @@ main args: basename := args[0] - driver := TrueColorPngVisualizer 524 240 basename --outline=0xffffff + driver := TrueColorPngVisualizer 800 240 basename --outline=0xffffff display := PixelDisplay.true-color driver display.background = 0xe0e080 @@ -45,6 +45,9 @@ main args: display.add (Png --x=268 --y=32 --png-file=heater-bw) display.add (Png --x=352 --y=32 --png-file=heater-white-bg) display.add (Png --x=436 --y=32 --png-file=heater-translucent) + display.add (Png --x=520 --y=32 --png-file=heater-4-bit --color=0x4080ff) + display.add (Png --x=604 --y=32 --png-file=heater-4-bit --palette-transformer=SwapRedAndBlack) + display.add (Png --x=688 --y=32 --png-file=heater-bw --color=0x20ffe0) display.add (Png --x=16 --y=120 --png-file=heater-uncompressed) display.add (Png --x=100 --y=120 --png-file=heater-4-bit-uncompressed) @@ -52,6 +55,9 @@ main args: display.add (Png --x=268 --y=120 --png-file=heater-bw-uncompressed) display.add (Png --x=352 --y=120 --png-file=heater-white-bg-uncompressed) display.add (Png --x=436 --y=120 --png-file=heater-translucent-uncompressed) + display.add (Png --x=520 --y=120 --png-file=heater-4-bit-uncompressed --color=0x4080ff) + display.add (Png --x=604 --y=120 --png-file=heater-4-bit-uncompressed --palette-transformer=SwapRedAndBlack) + display.add (Png --x=688 --y=120 --png-file=heater-bw-uncompressed --color=0x20ffe0) display.draw diff --git a/tests/gold/bitmap-2-visualized.toit.png b/tests/gold/bitmap-2-visualized.toit.png index 6cfa6ffd5cc6aa01b3e64d68a800ac030e245a64..e1fbc7d94fe3fb29d7243b35b8bfdbfae6adf60c 100644 GIT binary patch literal 11507 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 literal 7622 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 diff --git a/tests/gold/three-color-bitmap-2-visualized.toit.png b/tests/gold/three-color-bitmap-2-visualized.toit.png index 2085906e83467631bed4a158bc3fc882ba05466f..a40ab18e46209851c6168e92f93f421073deb5f8 100644 GIT binary patch literal 1403 zcmeAS@N?(olHy`uVBq!ia0y~yVAWt?U^HN228x_CE&m6kI0Jk_T>t<74`c%Ifs=DL z09EOFx;TbZ#J#z+P>{iZgTe5|nniE+2Z^tE9H8>=?s0jbS}54Q_}Qzjy%rZI6qgov-a--)paFc1eB_IGuN@}wu6A{a@Knl z@4bJB?DhB-x8nWe#TA(~r=~3PTxAnq#ikJF_BJP00>ZW6+g!(48DKOLx(%rF>J2f=?+k zU*pMKDS0XA{)B1F`>)OVtbeIm!+-yRJdin`PBeVIJ9&k;et(De>(tr1r$)Ux**kNo z*Uq$5rVEo8Vswue)&3}NpY`{*gQoG)JLV4-Ozl1w`#xY>w7&n^=#@ZgXTYrGJn%JW zv;U>L%zaTm7mHu!&U~BtGA!%n;-%+wLQk1$3pY$-c~G_Truxf?4E_14VM$MS2FEE+ znSSw9F#E1AZr*bx*L<0rmC6RRb&C^Y``S0*KNfhemQKwsE^+jp`*P|0JGKv2Og;Vd za#wN7C!?sysO*Qmt<$WGBVMXsJ;k#?l`*50sV}PXZTOG$mMDGwwWYk~U~M)A34fJe z{!I?L6gTY`Q|WIFKGmgXc*1^pZZ%@g@MMTm1O}wt{L7yWB_kT5^rN+N_1LyWzVgxk z`{W&?`&yG(s~v6k+usJISG`o{=ZUyKSuec8xjSH?&ees}=I3!o^-r-fjsiKBWq~SlM&N;kC6``W z&U`g7vYh52g^xQjEtjJs@H~qqGF3i!nsqdRt}Y# z0o?b~RvPd9GI=+o}<5tSh-WT6jk1TDz2?rnzrSV|Ej5y4&F=`xEN*jzqOgR z{xkQLs+-TAzJ3xA1P+Qu#@E$Br)#UEHaoW(-7C}C{%C&5grIab?Y~Q+W-@M3VOSe* z;7WemzLXu5erOlBaF5Q_)2E%D{qxo|UJMO$AyAla3%Z=O z=*!F~Ir$A~9^PjsxM{1VUSDq-_Kfjo3ETX077PkuNuUJw-etvoNsZKHQ{36s0Mozm zlBN3;b&uDt-6*6DLPqk3_&A%UMtE+Rl%N_Wv_>VOb1IAWMj+>)7m(w*K}eeuD15{V zP0jspv;M!?|LOPs-`DTk^T{0AJju52O<_vEa<$CO$0y|6zgcWLJ`o`_z-Yk43=~n4E(CHII0Jk_T>t<74`c%Ifs=DL z098Hqba4!+hIQv~| zid!ZK@dZdN4Fn=92QU{X;s@eNEqyLmoj&1_1&_wA*K*Ex>YiCEw(j?rz&$2b zPdx+|t(hV*(a5Fk-~6@%>K_dB-)YGHcza0OKGgG1*Y+uoSJqz6J|-W!Gv(4W-`7D( zOU-hZN_ll>1nT|@+Ou#E_tp4=lmGMhFV^2Rf9Hem!Iq+zE(h=}|K_`1QT*!Ltp$3O zr4wKKsq7w|q>eOfvlOF=WQH17GG`eBG&%np$;gimK?YE6d(qnWT3$ z%c^DZ4zZxkDfe7jwb}!Em&I~$8ilK9`Y-F8`E;(+)LUETMqOGe1$0u%ef6N-F*7#b zN~<(p_5RWK_SK$2lh%8jXXo^sdTPtmS(leb`L;he^iasxDH{LZ&&Q=FYlMqSN%ClMJ;rtkMzn(dS55K zREd2UywZd7mX3Si+wWg)8+v$^`kyxR06GrjgZhU}MyqbD4;0Ir=DZ^9^2@Uy z-@Z{#O|{xqE~@vfxt9o7TJ{>~g~6WCYXsu7N1a}lyvj`gUP-^K&JxLNgSD_0<_xafA;&nOooO;g^sOfj9ToMZP;;g zS;1@X{~O-$YxG`%AWm?;1Ln0|3r)NhgONEjQ@XuugDU#B$qSNo845V%{HE&vv4{on NJzf1=);T3K0RXx>`2zp| diff --git a/tests/png-visualizer.toit b/tests/png-visualizer.toit index 41b1509..df08a5d 100644 --- a/tests/png-visualizer.toit +++ b/tests/png-visualizer.toit @@ -9,6 +9,7 @@ import crypto.crc show * import host.file import monitor show Latch import pixel-display show * +import pixel-display.png import png-tools.png-writer import png-tools.png-reader show * import zlib @@ -403,3 +404,9 @@ byte-swap_ ba/ByteArray -> ByteArray: result := ba.copy byte-swap-32 result return result + +class SwapRedAndBlack implements png.PaletteTransformer: + transform palette/ByteArray -> none: + palette[0] = (palette[0] >= 0x80) ? 0 : 0xff + palette[1] = 0 + palette[2] = 0 diff --git a/tests/three-color-bitmap-2-visualized.toit b/tests/three-color-bitmap-2-visualized.toit index 700e04f..93b649b 100644 --- a/tests/three-color-bitmap-2-visualized.toit +++ b/tests/three-color-bitmap-2-visualized.toit @@ -23,7 +23,7 @@ main args: basename := args[0] - driver := ThreeColorPngVisualizer 440 240 basename --outline=BLACK + driver := ThreeColorPngVisualizer 610 240 basename --outline=BLACK display := PixelDisplay.three-color driver display.background = WHITE @@ -40,11 +40,15 @@ main args: display.add (Png --x=184 --y=32 --png-file=heater-2-bit) display.add (Png --x=268 --y=32 --png-file=heater-bw) display.add (Png --x=352 --y=32 --png-file=heater-white-bg) + display.add (Png --x=436 --y=32 --png-file=heater-bw --color=0xff0000) + display.add (Png --x=520 --y=32 --png-file=heater-2-bit --palette-transformer=SwapRedAndBlack) display.add (Png --x=100 --y=120 --png-file=heater-red-uncompressed) display.add (Png --x=184 --y=120 --png-file=heater-2-bit-uncompressed) display.add (Png --x=268 --y=120 --png-file=heater-bw-uncompressed) display.add (Png --x=352 --y=120 --png-file=heater-white-bg-uncompressed) + display.add (Png --x=436 --y=120 --png-file=heater-bw-uncompressed --color=0xff0000) + display.add (Png --x=520 --y=120 --png-file=heater-2-bit-uncompressed --palette-transformer=SwapRedAndBlack) display.draw diff --git a/tests/toit-png-tools b/tests/toit-png-tools index 8387669..ee472ac 160000 --- a/tests/toit-png-tools +++ b/tests/toit-png-tools @@ -1 +1 @@ -Subproject commit 8387669efa33a7108c746dcb19dd5ad0ac8f89d0 +Subproject commit ee472ac9d4333a138206f9d3a817d3c5432d6f87