Change colors assigned to unchanged values in a 2 dimensional array lazarus / 21-General ---------------------------------------------------------------------- Sheepdog · 2022-11-13 12:08 Suppose I have a 3x3 array with values... 1  1  1 1  2  3 2  2  2 ... and that I've set up a TImage to display that as 9 blocks of color in three rows... Top row: three blocks of red Middle row: a red block, a blue block, a green block Bottom row: three blocks of blue. So far so good, so far so easy. (Can we, for now, skip over whether I would "draw" on the bitmap, the canvas, a.n.other? I can do it. I just usually forget the pros and cons of those options!) --- Perhaps I should add that I am most interested in doing this in a computer running Windows. Of course, a general solution would be preferred, but this may be an OS-level project. Somewhere in the computer, there must be a table. === Now... the tricky bit: (Something I COULD do, years ago, on an Acorn Archimedes, by the way!) Interpret the terms used below as the human in the street would. I don't, for instance, mean "paint" quite as narrowly as Lazarus does. Somewhere, there must be a table that says the following... "If the byte storing the data for pixel[x,y] has 57 in it, make that pixel red, "If the byte storing the data for pixel[x,y] has 23 in it, make that pixel #5533FF (RGB values), "If the byte storing the data for pixel[x,y] has 42 in it, make that pixel mustard yellow... etc. (Of course, that "table of colors" could be stored in various data structures... and may not be accessible to you and me.) SIMPLY by changing, say, the first line, one datum, to... "if 57... make that pixel green", all the formerly RED pixels could nearly instantly be "switched" to green. === Is there a way to do such a thing in Lazarus? I won't need a "fully worked" answer... I hope! But if you could point me to the right words to use in Google searches, I'd be grateful. (On the Archimedes, this was called the palette.) === Why? On a modern machine, re-drawing my 3x3 grid is, of course, no great challenge. But I want to do a "time lapse" of "twenty bacteria moving around a Petri dish". On, say, a 255 x 255 grid. Each element of the array will have a number in it saying WHEN there was last a bacterium at that point. 0 at every x/y initially. (The pixels representing these x/y values to be black.) Then a 1 would be written to 20 places... the 20 places the bacteria start. Then a 2 in the places the bacteria move to in the second time slice. Then 3 for where they are at "time 3", etc. After 10 iterations, any x/y holding something <8 would display in black (empty bit of dish), x/y's with 7: very faint red, with 9: dull red, with 10: bright red. To go from interval 10 to interval 11 would NOT mean visiting every pixel, re-drawing it "by hand" to give it a new color to reflect the new "if it is this number... use this color..." needs. ---------------------------------------------------------------------- jamie · 2022-11-13 14:03 It seems you need specific data stored for each item and log some critical information for that item.   Maybe you could create yourself RECORD with all the items in it you need to track. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  Type MyCellRecord = Record     CurrentColor:TColor;     LastColors:Array[0..3] of Tcolor;    //Etc. End;  and In code Land Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Var   MyCells:Array[0..255] of Array[0..255] of TMyCellrecord;   From that, during your painting routines you can reference that. ---------------------------------------------------------------------- KodeZwerg · 2022-11-14 02:00 Quote from: Sheepdog on November 13, 2022, 01:08:12 pm(Can we, for now, skip over whether I would "draw" on the bitmap, the canvas, a.n.other? I can do it. I just usually forget the pros and cons of those options!) So how is your plan to change pixels color without touching the canvas? (based on your answer I could give better quality feedback) (That your way would be slow, pixel by pixel, I do not need to say, or?) ---------------------------------------------------------------------- Sheepdog · 2022-11-15 08:47 Thank you KodeZwerg... What I did last time and hope to find how to do again, is something I TRIED to explain in my first post... but, obviously, failed! Let me try again to explain that bit... Somewhere in the system... perhaps much "deeper" in the system than our Lazarus code... there is code and hardware that somehow translates the numbers in our abstract tables (2 dimensional arrays) into dots glowing brightly/faintly/not-at all on a monitor. (Or specks of ink on paper) On a monitor, the pixels are repeatedly re-drawn in a high-frequency cycle. Somewhere in that chain, a number goes in, and a mix of voltages is sent out to the R,G, and B elements of the pixel... three tiny LEDs. Somewhere ELSE, there is a... "table"?... that says "when the number normally meant to produce "red" is presented, set the voltages so that the red LED is as bright as possible and the blue and green LEDs are off". --- What I was able to do before, on the Archimedes was to tweak that "table". --- On some level, Windows machines have that capability, surely? Surely that's how the "night mode" screen is implemented, isn't it? And then there's the "calibration" that is possible? Admittedly, I want to "tweak" the table quite violently. And maybe I'll have to accept that I have to tweak the colors on the WHOLE SCREEN if I want to tweak just a bit of it... But I can live with that. I want to revise the "M numbers-to-3xM voltages" "table" so that if, say, 0 is sent to the subsystem, then off-off-off goes out to the LEDs being refreshed. Etc. === THANK YOU for pointing out the part of my question that made no sense. === Thank you, too, Jamie... yes! The code would indeed involve some such data structure. ---------------------------------------------------------------------- KodeZwerg · 2022-11-15 09:08 I do understand you now very clearly even if I have no idea about how- to... What comes in my mind would be a solution that affect the current running windows session... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button1Click(Sender: TObject);var  LKind: Integer;  LColor: TColor;begin  LKind  := COLOR_BTNFACE; // exemplary use that system color to tweak it  LColor := ClRed; // change above system color with ... that!  SetSysColors(LKind, LKind, LColor);end; That way you can change mostly system color to have it painted like however you want it to be. Next thing that comes in my mind would be to patch the palette of a bitmap to adjust any color on the fly but that can be a long struggle due the many amount of nearly same colored neighbors... But to tell my Graphics Adapter, hey fire now r/g/b/a on pixel at x/y with a width/height of x/y.... never done, never thought about, I am sorry. ---------------------------------------------------------------------- Sheepdog · 2022-11-15 16:26 Ha! Use the SYSTEM colors, like ButtonFace, and change what THEY are set to! I LIKE it! I'd really like to do it "properly", but that's just my New England, puritanical upbringing. "Going around" the barriers that are meant to fence you in is SO much more fun... and it will probably do what I want. === But! Then you went on to talk about changing the bitmap's palette. That might be exactly what I want to do! In the image, I'll only be using about 5 colors at any time. I don't think I'll HAVE any "nearly same colored neighbors". To put it differently: In state 1, my image... if we take a 3-pixel x 3-pixel "image" to illustrate the point... might look like...   Red  - Red  - Red   Red  - Blue - Green   Blue - Blue - Blue ... but then in state 2,   Blue - Blue - Blue   Blue - Red  - Yellow   Red  - Red  - Red ... ALL *still* based on the same NUMBERS in the 3x3 "defining" the image. But! Between State 1 and State 2, if I've got this right, the entry in the palette table that said... For the number which when sent to PaintPixel gave Red before, NOW make PaintPixel make the pixel Blue... (and also... For the number that when sent to PaintPixel gave Blue before, NOW make PaintPixel make the pixel Red, and For the number which when sent to PaintPixel gave Green before, NOW make PaintPixel make the pixel Yellow... --- If you can tell me that might be even half right, you will have been a great help... I will head off to Google much encouraged. I've already found... http://www.delphigroups.info/2/56/200775.html ... which seems like it might point the way towards how to do what I'm trying to do? ---------------------------------------------------------------------- Sheepdog · 2022-11-15 19:53 Hmmm. Further to above... An hour's struggle, no complete answer yet. It  seems I need to build a "palette" in  memory... Seems simple enough. And then tell my TImage object... or its canvas's... or... ?... ANYWAY, tell SOME bit map "here's your palette. Use this. That's where my work breaks down at the moment. Specifically, when I try to use "SetDIBColorTable", I'm told "Indentifier not found". The could AROUND  that line has be suspecting that using SetDIBColorTable isn't the only path to success. But I've yet to figure it out. Help? Either with moving forward from there, or starting again further back? ---------------------------------------------------------------------- Sheepdog · 2022-11-15 20:18 (If you try using the change system colors route... go slowly!!! You WILL mess up your whole screen if you get it to work! You need to see how to put it back to normal, too! Stick to Color_BTNFACE. That's normally $C0C0C at Nov 22. Also: You'll need to put LCLIntF in your "Uses" clause. And maybe LCLType, too! --- PS: ARGH! So NEARLY works: Set up an image with some bits drawn (..pixels[x,y]:= in BtnFace color. Then used the "change system color" code. And everything across all apps, all parts of the screen INSTANTLY changed to the new color... EXCEPT for the part of my bitmap drawn previously in the BtnFace color! ---------------------------------------------------------------------- KodeZwerg · 2022-11-15 20:33 Last time that I play with Palette manipulation was with Delphi long ago. Here is a snippet that lead you the way (I hope) This worked with Bitmaps having 2, 16 or 256 Colors palette, not more(!) Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Procedure MapColor(Const ABitmap : TBitmap; Const AOldColor, ANewColor : TColor);Var  i, j,  lPaletteEntries : Integer;  lPalette : TDIBPalette;  lLastGDIObj : HGDIOBJ;  lQuadOldColor,  lQuadNewColor : TRGBQuad;  lWordOldColor,  lWordNewColor : Word;Begin  Assert(Assigned(ABitmap), 'MapColor(): ABitmap is not assigned!');   lQuadOldColor.rgbRed := GetRValue(ColorToRGB(AOldColor));  lQuadOldColor.rgbGreen := GetGValue(ColorToRGB(AOldColor));  lQuadOldColor.rgbBlue := GetBValue(ColorToRGB(AOldColor));  lQuadOldColor.rgbReserved := 0;   lQuadNewColor.rgbRed := GetRValue(ColorToRGB(ANewColor));  lQuadNewColor.rgbGreen := GetGValue(ColorToRGB(ANewColor));  lQuadNewColor.rgbBlue := GetBValue(ColorToRGB(ANewColor));  lQuadNewColor.rgbReserved := 0;   If ( ABitmap.PixelFormat In [pf1Bit, pf4Bit, pf8Bit] ) Then  Begin    lLastGDIObj := SelectObject(ABitmap.Canvas.Handle, ABitmap.Handle);  Try      lPaletteEntries := GetDIBColorTable(ABitmap.Canvas.Handle, 0, 256, lPalette);      If ( lPaletteEntries > 0 ) Then      Begin      For i := 0 To ( lPaletteEntries - 1 ) Do          if CompareMem(@lPalette[i], @lQuadOldColor, 3) then            lPalette[i] := lQuadNewColor;        SetDIBColorTable(ABitmap.Canvas.Handle, 0, lPaletteEntries, lPalette);      End;    Finally      SelectObject(ABitmap.Canvas.Handle, lLastGDIObj);    End;  End  Else  begin    // wrong bitmap, more than 256 colors...  end;end; Good luck on your journey! ---------------------------------------------------------------------- BobDog · 2022-11-15 23:21 Windows, using the windows rgb macro, you can use pointers to switch components. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i useswindows; constred=0;green=1;blue=2;  function switch(c:colorref;a,b:byte):colorref;var temp:byte;begintemp:=pbyte(@c )[a];pbyte(@c)[a]:=pbyte(@c)[b];pbyte(@c)[b]:=temp;exit(c);end;     var   color:colorref; begincolor:=rgb(200,100,50);writeln(getrvalue(c olor),' ',getgvalue(color),' ',getbvalue(color)); color:=switch(color, red,blue);writeln(getrvalue(color),' ',getgvalue(color),' ',getbvalue( color)); color:=switch(color,green,red);writeln(getrvalue(color),' ',getgvalue(color),' ',getbvalue(color)); writeln('Press enter to quit . . .');readln; end.  (If it is of any use to you) ---------------------------------------------------------------------- Josh · 2022-11-15 23:25 if the data representing the info to displays alters with each sample to indicate the new sample then the pallete would stay the same as the data is different, you just then need to draw the pixels to the canvas with your custom colormap/palette. drawing a 255x255 pixel grid with custom colorpalletee is very fast when direct accessing pixels. sub 1ms sample prog attached, most of the code is for the simulation. hope you can make sense of it. ---------------------------------------------------------------------- Sheepdog · 2022-11-16 08:20 KodeZwerg- Thank you! That looked very much like what I am looking for. But I note what you said about "it was a long time ago... and in Delphi"... THANK YOU for TRYING! I tried to compile it, and came up against... Error: Identifier not found "TDIBPalette" Error: Identifier not found "GetDIBCoIorTabIe" Error: Identifier not found "SetDIBColorTable" (And yes, I have both LCLType and LCLIntF in my Uses clause! === So Google searching was not encouraging. For others with more moral fibre, the following may help... there's a reference to GetDIBColorTable, at least,  in that. https://fossies.org/linux/fpcbuild/fpcsrc/packages/winunits- jedi/src/jwawingdi.pas === (Others... Thank you, too! I'm turning to your suggestions now!) ---------------------------------------------------------------------- Sheepdog · 2022-11-16 08:43 STOP PRESS- I wrote the following too quickly! I've been getting into the code. It isn't QUITE what i GUESSED in the following. Will edit again one day. When I get out of the code!. While writing: Thank you KodeZwerg for "put Windows and Graphics in Uses"... Duh! Sorry I didn't find that for myself.) Josh! Great fun! Thank you... if only for sight of your creation. (I commend it to other readers, if just for having the fun of watching the display!) Let me see if I have this right: The left hand image shows the whole area with a static set of "the code for this pixel is (a random number from (incl) 0 (or 1) to 9", just because it is pretty, and perhaps it was a stage in the program's development. The right hand image is your petri dish simulation. The "code for this pixel" array driving that started with mostly 0's, but with three areas with a cluster of 1-9's. And then, as the simulation goes from generation to generation, NOT entirely random other array elements switch from 0 to a number from 1-9. And, simply to give "life" to the displays, you use your "tricks" to do what I want... you make the color appearing at, say, a pixel with "3" "behind" it change, over and over again? === Ironically, what I am trying to do is also a simulation of life on a petri dish. ---------------------------------------------------------------------- KodeZwerg · 2022-11-16 08:49 uses windows, graphics Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TDIBPalette = array[0..256] of TRGBQuad; Procedure MapColor(Const ABitmap : TBitmap; Const AOldColor, ANewColor : TColor);Var  i,  lPaletteEntries : Integer;  lPalette : TDIBPalette;  lLastGDIObj : HGDIOBJ;  lQuadOldColor,  lQuadNewColor : TRGBQuad;Begin  Assert(Assigned(ABitmap), 'MapColor(): ABitmap is not assigned!');   lQuadOldColor.rgbRed := GetRValue(ColorToRGB(AOldColor));  lQuadOldColor.rgbGreen := GetGValue(ColorToRGB(AOldColor));  lQuadOldColor.rgbBlue := GetBValue(ColorToRGB(AOldColor));  lQuadOldColor.rgbReserved := 0;   lQuadNewColor.rgbRed := GetRValue(ColorToRGB(ANewColor));  lQuadNewColor.rgbGreen := GetGValue(ColorToRGB(ANewColor));  lQuadNewColor.rgbBlue := GetBValue(ColorToRGB(ANewColor));  lQuadNewColor.rgbReserved := 0;   If ( ABitmap.PixelFormat In [pf1Bit, pf4Bit, pf8Bit] ) Then  Begin    lLastGDIObj := SelectObject(ABitmap.Canvas.Handle, ABitmap.Handle);  Try      lPaletteEntries := GetDIBColorTable(ABitmap.Canvas.Handle, 0, 256, lPalette);      If ( lPaletteEntries > 0 ) Then      Begin      For i := 0 To ( lPaletteEntries - 1 ) Do          if CompareMem(@lPalette[i], @lQuadOldColor, 3) then            lPalette[i] := lQuadNewColor;        SetDIBColorTable(ABitmap.Canvas.Handle, 0, lPaletteEntries, lPalette);      End;    Finally      SelectObject(ABitmap.Canvas.Handle, lLastGDIObj);    End;  End  Else  begin    // wrong bitmap, more than 256 colors...  end;end; ---------------------------------------------------------------------- Josh · 2022-11-16 09:43 for each frame update each pixel color = colormap of the value of the data in the 2d array. both images have there own data and colormap. image on left is updated with random data 0-255 for each data element, as its using a 256 color map, hense it looks like tv snow, the right pictue uses a colormap, but the data it displays is my simulation of growth, ie it populates some areas with values when it starts, then every interval calculates new data for growth and death based on the data and displays that data.ie if a a value of 1 is found it may either increase it or decrease it back to 0 (but looks one pixel around each time to do the same to them) etc. This uses a nonmodifying colormap when its running. if your data held 1--9 values, you could just change the color map each time before you call the paint routine, so that ie 1--5 were black 6,7,8,9 what ever color, then next time change color map to 1--3 are black 4--9 whatever color. its flexible either way, and willnot effect the speed of display. the paint routine changes the color of each pixel based on a colormap lookup of the value of the array hope that makes sense. ---------------------------------------------------------------------- Sheepdog · 2022-11-16 23:50 Josh- Yes! Your reply #14 DOES make sense. And is very encouraging. And you have guessed what I wanted to do. The news that I CAN (probably!) do it with little change to your code is excellent news. Now all I need is a few hours to spend on this. I rather "overspent" the "time for working on the program" budget for today. Will have to attend to other things for two days now... but looking forward to getting back to this soon! qsm