Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Background: Mechanism: RECT
#1
The tag "rect" is very useful in background files, because using them does not require a graphic file, thus saving space, and we often need areas with a single color. In the original LF2, stage (1-5) backgrounds and HK Coliseum have used RECT. However, it is a little bit more advanced to understand what every RECT codes the color they represent. For those who tried to perform experiments on it, it may be found that the actual color "jumps here and there" in the RGB cube as RECT value changes, leaving no interrelationship seems to be found.

This "tutorial" explains the mechanism behind RECT codes in background files, as well as providing two-way formulas exchanging RECT codes and permitted RGB codes.

For quick reference, you may want to read the Summary. You can easily see this is one of the longest article in this forum, but if you have some spare time, it is recommended to start from Part 1 or 2 at least, which describe the mechanics more detailed so you will have a concept of how the formulas work.


From here on, italics will be representing my hypothesis and some points I am not completely sure, but generally I think they do make sense, or else I won't even keep them here. Just to notify.



~~~ Table of Contents ~~~
  • Part 0 - Preface
  • Part 1 - RGB
  • Part 2 - The 1-Dimensional Notation
  • Part 3 - Linear Color Codes
  • Part 4 - Examining RECT Basics
  • Part 5 - Secrets Disclosed in Binary RECT
  • Part 6 - The Jackpot and R'G'B' to RGB
    • 6.1 - 777 Jackpot
    • 6.2 - B' and B Exchange
    • 6.3 - R' and R Exchange
    • 6.4 - G' and G Exchange
    • 6.5 - R'G'B' to RGB
  • Part 7 - RECT to RGB and its Inverse
    • 7.1 - RECT to RGB
    • 7.2 - RGB to RECT
    • 7.3 - R, G, B to RECT
    • 7.4 - RECT to R, G, B
    • 7.5 - Handling Negative RECT
  • Part 8 - Exceptional Cases
  • Part 9 - Mechanical Summary and Applications
    • 9.1 - Overview of Formulas
    • 9.2 - The Exchange Cycle
    • 9.3 - Formulas for Secondary Base Colors
  • Summary



Part 0 - Preface

Spending a short while saying why I write this: I was too searching what a certain RECT represents a RGB, or more ideally the interchange formulas so that I can type in the color I want. I have found YinYin had wrote something about it, I guess a long while ago. The link to his "extremely professional explanation", provided by Marshall, is dead, so I was not certain what its content is. The passage by YinYin can be found now so far is this (click). Maybe this is actually that explanation, but I just can't make sure. Anyway for this explanation, compared to the theory now I have made up below, contains some errors. I know he had wrote a program for us to get RECT codes by selecting a color, but I could not install it, so I was looking for a manual translation. I could not completely understand RECT just after reading his explanation, until I had done further experiments. However, it surely clarify the biggest concept of the RECT mechanism (see Part 2), so I could test samples on the correct way very soon. Now this tutorial will fix the remaining minor errors in YinYin's explanation, and show you the system as detail as I can.



1
Part 1 - RGB

For some catch-up I explain also the RGB codes. If you think it is fine you may skip this.

"RGB" stands for simply Red-Green-Blue, a code system representing digital color by notating the three basic optical colors red, green and blue, which could mix together to other colors. RGB codes are usually represented by a set of three decimal (base-10) numbers or a single number made up by three groups of hexadecimal (base-16) numbers. Today's computer systems and applications generally support a word size of 32 bits; when applied to RGB codes, these 32 bits will be divided into 4 parts: 3 parts of 8 bits each for the 3 colors, and the remaining part is for alpha (transparency; just put this aside since LF2 and *.bmp files do not support alpha scales). One bit means a binary (base-2) digit which stores either 0 or 1 only, i.e. 2 possible values, then 8 bits combined will be 2^8 = 256 combinations. Three colors of 256 combinations each will be 2^24 = 256^3 = 16,777,216 distinct colors.

Here comes to the end of this part. There will be some further RGB-related contents later.



2
Part 2 - The 1-Dimensional Notation

This is all about combining arrays with multiple dimensions into linear arrays, or more than one variable into one. RECT works in this way too; but before mentioning RECT, I first provide you several examples of what this term is about, so after this step-by-step bridging, you will understand RECT exactly when we approach there. I assume you have some basic Data Changing knowledge when reading this, so you should have no problem understanding the terms in the following examples.

Let's repeat. This is a technique when we need to combine arrays more than one dimension, into one. A simple example would be stage/id:. Excluding the latest Survival Stage, we know that id: 0 is Stage 1-1, up to id: 9 is Stage 1-10, then id: 10 is Stage 2-1 ... , all until id: 49 which is Stage 5-10. Now pay attention. (1) Stage X-Y contains two variables which store integers, where 1 <= X <= 5 and 1 <= Y <= 10; but (2) id: only recognize one integer variable ranged 0 to 49 inclusive, instead of something like catchingact: which recognize two variables. For normal players (1) makes sense to them, and imagine LF2 reads your stage selection input after confirming fighters the first time into variable X, then Y starts from 1 at any conditions, later Y is assigned to be (Y + 1) if an act is completed, and so on. However, we data changers know that stage.dat actually only list the stage acts with one id:, which is case (2). The stage selection in the game interface actually reads and translate the id: into 0, 10, 20, 30, 40 for Stages 1 to 5 or 50 for Survival. Translation between the 2-D imaginary system and the 1-D actual system would be like

Code:
Two-Dimensional Array [1..5, 1..10]

[1, 1]  [1, 2]  [1, 3]  [1, 4]  [1, 5]  [1, 6]  [1, 7]  [1, 8]  [1, 9]  [1, 10]

[2, 1]  [2, 2]  [2, 3]  [2, 4]  [2, 5]  [2, 6]  [2, 7]  [2, 8]  [2, 9]  [2, 10]

[3, 1]  [3, 2]  [3, 3]  [3, 4]  [3, 5]  [3, 6]  [3, 7]  [3, 8]  [3, 9]  [3, 10]

[4, 1]  [4, 2]  [4, 3]  [4, 4]  [4, 5]  [4, 6]  [4, 7]  [4, 8]  [4, 9]  [4, 10]

[5, 1]  [5, 2]  [5, 3]  [5, 4]  [5, 5]  [5, 6]  [5, 7]  [5, 8]  [5, 9]  [5, 10]
Code:
One-Dimensional Array [0..49]

[0]   [1]   [2]   [3]   [4]   [5]   [6]   [7]   [8]   [9]

[10]  [11]  [12]  [13]  [14]  [15]  [16]  [17]  [18]  [19]

[20]  [21]  [22]  [23]  [24]  [25]  [26]  [27]  [28]  [29]

[30]  [31]  [32]  [33]  [34]  [35]  [36]  [37]  [38]  [39]

[40]  [41]  [42]  [43]  [44]  [45]  [46]  [47]  [48]  [49]   ...

For 2-D [X, Y] and 1-D [Z], translation: Z = 10 (X - 1) + Y - 1

Their difference is that, cells of the 2-D system are linked like a net -- five horizontal linking lines and ten vertical, but the 1-D system is actually a single line like [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] ... [48] [49]. It seems that from [0] to [10] is only one vertical block apart, but actually there is no vertical leap in a 1-D horizontal line; the leap is actually 10 horizontal blocks. Ideally, [anything, 1] is where you may start a stage, but actually it is [anything divisible by 10]. The locations allowed to start Stage Mode are marked { }, and the whole line would looks like

{0} [1] [2] [3] [4] [5] [6] [7] [8] [9] {10} [11] [12] [13] [14] [15] [16] [17] [18] [19] {20} [21] ... [29] {30} ... {40} [41] ... [48] [49]

You see the locations are greatly apart but with constant interval, because one interval means one cycle of completion of the smaller digit. Here it is the imaginary Y variable. In translation, Z changes as Y changes, but Z moves a whole interval (10, which contains all possible values of Y) as X changes only by 1. This is just like the minute hand and second hand of (some types of) clocks. When the second hand completes a cycle, 60 units, the minute hand will move one unit.

Concerning time, we can talk about TU in DC too. Randomly say, if I want a wait: value of 3:31:16 (3 minutes, 31 seconds and 16 frames in 30 fps), I need to combine all 3 numbers into TU. Examine the cycles first: 30 frames (TU) = 1 s, 60 s = 1 min; 31 s = 930 TU, 3 min = 180 s = 5,400 TU. Sum together, it is 6,346 TU. This seems a bit off-topic, but I want to say, a change of unit can make multiple variables into a single possible. For time values the exchange of units is obvious, 1 hour = 60 min = 3,600 s = 108,000 TU, but for some systems -- more on-topic -- colors, it is not. How can I say 256 units of blue = 1 unit of green? This is ridiculous.

Really? No, in fact human can define many things. The original LF2 has 5 acts for each stage only. It is perfectly possible for Wong to define id: 0 to 4 for Stage 1, up to id: 20 to 24 for Stage 5. In this case every 5 id: units exchange to 1 stage, and the act number repeats the cycle and returns to the start again. When it moves to color, it is just the same -- when one cycle completes, another moves 1. Let's see RGB first.



3
Part 3 - Linear Color Codes

As mentioned in Part 1, RGB may be one hexadecimal number. For example, the largest, static blue area on the top of the LF2 screen listing HP MP bars, that blue is coded #324D9A. In the three-variable decimal notation it is (50, 77, 154), which is the decimal numbers of 32h, 4Dh and 9Ah. But what if we directly exchange 324D9Ah into decimal? It will be 3,296,666, which seems to be meaningless, but it will not if we do some calculations. Why we can directly breakdown 324D9Ah into 32h, 4Dh and 9Ah, is because each color's available depths is 256, from the 8 bits per color system (see Part 1), and 256 is delicately 100h. So now

Code:
-    32     h
-   * 10000 h
-      4D   h
-     * 100 h
-   +)   9A h
-   -------
-    324D9A h

324D9Ah = 32h * 10000h + 4Dh * 100h + 9Ah

           50 * 65536  +  77 * 256  + 154 = 3296666

After breaking down the number, it looks just the same no matter we change all of its elements into decimal or not.

Let's try notating a number in RGB and 1-D decimal simultaneously. Say, we start from color #000000, and represent it by 0. If we treat the code as one number and add it by 1, it will be 1 or #000001, which is R0 G0 B1. Continuing the process, until 255 or #0000FF, separately it is R0 G0 B255. Next it is the critical point, where degree 255 of blue color "transform" into 1 green point; it is 256 or #000100, R0 G1 B0. The previously said weird color transform occurs. It is indeed weird if you move one unit by one unit on this line ranged from 0 to 16,777,215 which intend to represent a 3-dimensional array. If you move 3-D-ly, that is, cycle by cycle, it should be just fine. Let's go back to the #000100 code. Normally you adjust the base color directly, like adding degree 100 of green. Now one cycle of blue is 256 units, so every 256 (16^2) units in whole means degree one of green. Degree 100 would be 100 * 16^2. Add it to the #000100 or 256, will become #006500 or 25,856. Similarly, if I want another degree 16 of red, it is 16 * 16^4 = 1,048,576; add to the code it will be #106500 or 1,074,432.

"Degree 256 of blue means degree 1 of green" still looks not very natural; we should rather say: degrees of red, green and blue moves in steps of 65,536, 256 and 1 unit respectively on the 1-D RGB color representation. To "decrypt" an 1-D color code, conversely we see how many steps of a certain base color it has taken. Take the previous #324D9A, in hexadecimal it is simple since red and green are moving in steps of 10000h and 100h units respectively. In decimal, it is still not that complicated, just require some division.

3296666 / 65536 = 50 ... 19866

So it has taken 50 steps of red, which is 65536 units each, and something is left. Process that number,

19866 / 256 = 77 ... 154

Thus 77 steps of green, 256 units each, is taken. The remainder represents blue degree since it is 1 unit each.

You may wonder why I tell so much on RGB instead of our subject RECT, and even use such an unpopular decimal method to notate RGB codes. Well, the RECT mechanism is quite similar to this. We are approaching, but there are still some points to be clarified.



4
Part 4 - Examining RECT Basics

From YinYin's explanation, we (or I) learn that RECT codes are lying on an 1-D line representing sets of 3-variable color codes; from it and also Part 3, we learn that if we put 3 color degrees into 1-D, 2 of the colors need to be represented by leaping certain intervals. Therefore, before experimenting we expect only one base color will change if we change the RECT value by certain amounts which are or are multiples of its intervals. The following are the actual samples I have taken using LF2.exe, LF2 Data Changer and Paint.

Code:
rect   R   G   B
------ --- --- ---
Code:
0      7   7   7
32     7   11  7
2015   7   255 255
2016   8   3   7
2048   15  7   7
4032   15  255 7
4063   15  255 255
4064   16  3   7
4095   16  3   255
4096   23  7   7
5984   23  243 7
6000   23  243 135
6016   23  247 7
6048   23  251 7
6079   23  251 255
6080   23  255 7
6090   23  255 87
6100   23  255 167
6110   23  255 247
6111   23  255 255
6112   24  3   7
6143   24  3   255
6144   31  7   7
6368   31  35  7
6751   31  79  255
6752   31  83  7
65503  255 255 255
65504  0   3   7
65535  0   3   255
65536  7   7   7
71646  23  255 247
262144 7   7   7

You may find many equal values within one base color, and one seems to be out-of-pattern phoneme in red code (often jumps in 7 but very occasionally in 1) which will be explained in lower parts. Now let's follow my interpretation of the data collected.

First, we can see the smallest changing in RECT represent the blue code, i.e. when RECT changes by one, it is likely only blue depth changes. Then, we see RECT is similar to RGB in fact -- red using largest intervals and green using moderate intervals, lastly for blue -- refer to RECT values 6079 to 6112, we see green value changes as blue completes a cycle; finally red is the largest, by comparing RECT: 0 and 2048, only red changes while green and blue are kept at 7. So, now we discover that in RECT, the base colors red, green and blue moves in steps of 2,048, 32 and 1 unit respectively.

Green in steps of 32... this actually means there are 32 degrees of blue depth levels available in total. 32 is 32 divided by 1; similarly for green cycles, there are... 2,048 / 32 = 64 depth levels. This looks inconsistent but true, because green depths jumps in 4 in RGB, 256 divided by 4 that is 64; and for blue it jumps in 8, so only 32 depth levels are available. For red, first we must find out how much is one cycle of it. If you have noticed RECT: 0 and 65536 has all the same RGB representation, you know the answer. Everything in RECT repeats in cycles of 65,536 units. Lastly, 65,536 / 2,048 = 32 depth levels for red. (There are only 32 designed depths for red, not 64 as it seems. The reason of the phoneme will be discussed in Part 6.) This should clarify the error in YinYin's explanation, "goes from 7 to 255 in steps of 4, thus leaving you 32 colors." (In fact if a sequence goes from 7 to 255 in steps of 4, it has 63 members.) Only red and blue "go from 7 to 255" and "leave you 32 colors," and only green goes "in steps of 4."

To shortly conclude, RECT is a 1-variable color code (designed to) provide 32, 64 and 32 red, green and blue color depths respectively. When representing in 1-D, red and green values are notated by steps of 2,048 and 32 units each.



5
Part 5 - Secrets Disclosed in Binary RECT

It is confusing to jump in 2,048 and 32 intervals to obtain the color depths. You may wonder is there a method to split the number into three parts, one base color each, as RGB do (324D9Ah is one hex number, but we can directly say it is R32h, G4Dh and B9Ah). Besides, you may want to learn more about why the available depths are allocated so strangely, why 32, 64, 32 but neither all-32 nor all-64.

For the first question, it is a matter of numerical base. RGB can do so because the cycle is 256, which can be written in powers of 16, the base it applied, therefore when it comes to division as in Part 3, it just looks the same as sticking all the digits into one number under base-16. For the acts of stages, both the base and cycle are 10, so using base-10 would obviously creates the similar effect. Now it is tricky when it comes to RECT. We need a base fits both the cycles of 32 and the cycle of 64. There is one -- considering 32 = 2^5 and 64 = 2^6 -- it is base-2, the binary system. You will see how it works after transforming RECT codes into binary. Let's see,

Code:
rect   R   G   B    rectBin (16 bits)    R' G' B'
------ --- --- ---  -------------------  -- -- --
Code:
0      7   7   7    0000 0000 0000 0000  0  0  0
32     7   11  7    0000 0000 0010 0000  0  1  0
2015   7   255 255  0000 0111 1101 1111  0  62 31
2016   8   3   7    0000 0111 1110 0000  0  63 0
2048   15  7   7    0000 1000 0000 0000  1  0  0
4032   15  255 7    0000 1111 1100 0000  1  62 0
4063   15  255 255  0000 1111 1101 1111  1  62 31
4064   16  3   7    0000 1111 1110 0000  1  63 0
4095   16  3   255  0000 1111 1111 1111  1  63 31
4096   23  7   7    0001 0000 0000 0000  2  0  0
65503  255 255 255  1111 1111 1101 1111  31 62 31
65504  0   3   7    1111 1111 1110 0000  31 63 0
65535  0   3   255  1111 1111 1111 1111  31 63 31
65536  7   7   7    0000 0000 0000 0000  0  0  0
71646  23  255 247  0001 0111 1101 1110  2  62 30
262144 7   7   7    0000 0000 0000 0000  0  0  0

From here, we first define another type of color code, R'G'B', used in RECT. It is just like RGB but with R' and B' ranged 0 to 31, and G' ranged 0 to 63 inclusive, instead of 0 to 255. 0 is the black end and their greatest number are their colored end. This system will be mentioned and used for quite a while, and no replacement until we discuss how to link R'G'B' to RGB.

Binary numbers are too long and normally divided into groups of 4. In the current case, for our purpose you may group them in to 5, 6, 5, which would be even clearer; e.g. 00001 111110 00000 for RECT: 4032. Under this division, you can clearly see the last three columns of the above table is just the decimal representation of the three parts of the binary number, which is transformed from RECT. For the example just given, 00001 is 1, 111110 is 62, and 00000 is 0. RECT only recognize 16 bits, thus for values starting from 65536 (2^16), the cycle is just repeating. The exceeded amounts are ignored, see the last three sets of data in the table.

Digital memories group 8 bits as 1 byte, thus it is normal to expect processing word sizes or the smallest memory units are in multiples of 8 bits, e.g. 8, 16, 24, 32, 64 or 128. If RECT adapts an all-32 allocation of color depths, only 15 bits will be used and 1 bit will be wasted. 1 bit means a lot -- a double of distinct colors available. I guess LF2 originally took this way in fact, until later changed green to have 64 levels, because we see all the starting points are 7 in RGB (see R'0 G'0 B'0 = R7 G7 B7), and G'63 = G3 which looks strange. If the authors were intended to use all 16 bits at the very start, the ranges RECT available in RGB should be 7 to 255, 3 to 255 and 7 to 255 as R'G'B' goes from 0 to 31, 0 to 63 and 0 to 31 respectively, instead of the current pattern of green that from 7 to 255 then completes on 3. If you acknowledge RECT runs in 16 bits, all-64 depths would be impossible since it requires 18 bits. Finally, for the reason why it is green being doubled in depth levels but not red or blue... other than the preferences of the authors it may be a matter of symmetry. Among all the combinations of two 5's and one 6, 5-6-5 looks better than 6-5-5 or 5-5-6.

So now, if we think in binary form of RECT or R'G'B', it is much more clearer to see the advancement from cycle completions. When B' completes a cycle of 0 to 31, when it back to 0 G' will increase by 1. In binary, 32 is 100000, where the 1 is the sixth digit (count from the right), it is over the bound of the "blue area". Listing the whole binary RECT, it is 00000 000001 00000. For the sixth to eleventh digits, it is the business of green; for the last five digits, it is for red. Maybe you remember I said before this "some blue points transform to one green point" "advancement" is nonsense (Part 3). The replacement system is to move 3-D-ly or cycle by cycle. Here when you adapt this system, you change either R', G' or B', or say the 12th to 16th, 6th to 11th or 1st to 5th digits of the binary RECT in groups. The purpose we break RECT down to binary is to enable direct base color changes as in hexadecimal RGB. Use it, or think in this way.

Recalling the basics; digital colors made up of three equally ranged base colors. We still have to transform R'G'B' code, which derived from RECT, to RGB in order to realize the colors digitally. When we have some concepts on RECT mechanism, we can now discuss how RECT related to RGB.



~


~
6
Part 6 - The Jackpot and R'G'B' to RGB

(For the meaning of R'G'B' which defined by me, please refer to the paragraph just below the table in Part 5 if you have skipped it.)

When you see the starting point R'0 G'0 B'0 represents R7 G7 B7, ever think of the 777 Jackpot? But it is a joke to say the authors were inspired by the Jackpot in developing RECT. I can't say the possibility is 0%, but there are just reasons which are much more logical and reasonable, also are likely to replace it.

In Part 4 we concluded RECT is a linear color representation designed to provide 32 red, 64 green and 32 blue color depths, and in Part 5 I proposed the depth levels available for green was originally also 32 as other colors, until further optimization. At this moment we ignore the 64 depths and assume the system adapting all-32 depths. We are going to discuss why and how a 0 to 31 scale transform into a 7 to 255 scale.

6.1
6.1 - 777 Jackpot

The RGB system has 256 depths for each color, which is exactly 8 times of the degree-32 one. Even its density is much lower than RGB, it still has to spread evenly on the full scale from black to the corresponding base color. Then although the colors are far less detail than RGB, it still provides a variety of colors. To spread, we just multiply it by how much times its scale is less denser, 8 times. In a quick calculation, the scale 0 to 31 would become 0 to 248, so now it represents absolute black (0) and nearly approach the full saturation (255). It was 0 to 31 in steps of 1, now becomes 0 to 248 in steps of 8; both have 32 steps. Comparing 0-248 and 7-255, there is a difference of 7 units. The reason why having a value of 0 in RGB scale is avoided, is that at least one color with 0 can be produced by using nothing in backgrounds -- absolute black (#000000), as said by YinYin. This is important because LF2 background files had a limit of 30 layers only before Ver 2.0. Saving one layer saves a lot. On the other hand, absolute white (#FFFFFF) is one of the most basic colors other than red, green and blue, so the scale is sticking to the 255 end to make this color available. When the right end is 255, the left end, 31 steps apart with 8 units each, is 7, so creating the Jackpot starting.

6.2
6.2 - B' and B Exchange

So now we have discussed why RECT is roughly representing degrees 7 to 255 in the RGB scale. Let's start to see how to exchange the two system. Out of the three base colors, only blue in the actual R'G'B' or RECT system is exactly the same as the degree-32 old system we have just discussed. But anyway let's see how to transform B' into B of RGB by a formula. I am giving it out first; it is

B = 8B' + 7

8 is the magnification, and 7 is the constant such that RECT could give out a depth of full saturation (255). When B' is 0, B is 7; when B' is 1, B is 15; when B' is 2, 23; and so on. Now suppose we have selected a color and want its RECT, we need a inverse formula. It could be easily got by changing its subject, as follows

8B' + 7 = B

8B' = B - 7

B' = (B - 7) / 8

Furthermore, we discover something; both RGB and RECT values are integers, so in this formula, we can see what is the condition for a certain color depth being available in RECT, using a mathematical method. RECT must be an integer, therefore B' must be also. By using the formula, the statement will be "(B - 7) / 8 must be an integer," and can be rewritten as "(B - 7) / 8 must be divisible by 1." Multiply the whole line by 8, we will get

(B - 7) must be divisible by 8.

This presents our concepts on only using 7, 15, 23, 31, ... for B' in terms of RGB in a generalized way.

6.3
6.3 - R' and R Exchange

Next, we will temporarily skip green and first move on to the red code. Referring to the table in Part 4 or your own samples, R' should represent at most 64 distinct color depths in normal cases. They can be grouped into two sequences: the first being 7, 15, 23, ..., 247, 255 the same as blue, another one is adding this sequence by 1, into 8, 16, 24, ..., 248, 0. Sorry to delay (very bit though), but this mysterious +1 is related to the green code and thus will be discussed later. For now, please ignore it, so 7 and 8 are both treated as 7, 15 and 16 are both 15, etc. If you can tell yourself to accept this, then the story will be extremely simple, because it is just a copy and paste with little altering:

R = 8R' + 7

R' = (R - 7) / 8

(R - 7) must be divisible by 8
, under certain conditions.

They indeed look very familiar. Done, now the last color.

6.4
6.4 - G' and G Exchange

To be mentioned last, the green code must be the most complicated one, being it was optimized into a cycle differs from other base colors, in the development of RECT. There could be two interpretations of the green code; the more direct one is, similar to red and blue, spread the scale only instead in 64 intervals. The very first problem we face would be what the highest G' value represents in G of RGB -- G'62 is already G255, and then G'63 is G3. The formula can't work this way as far as we know now. But take a look, remember they are cycles, so it is not a great deal to use 259 to replace 3. If we say G'63 is G259, the problem is partly solved, and the formulas will be

G = 4G' + 7

G' = (G - 7) / 4, where 7 <= G <= 259 and

(G - 3) must be divisible by 4.

Our only concern now would be getting a 259 output for G when we input G' as 63 in the first equation, as mentioned before. In look for a mathematical representation, there is a solution, but first I have to introduce a pair of arithmetic operators. (Arithmetic operators are symbols marking arithmetic operations, e.g. +, -, * and /.) They are div and mod. They are division-related, while div returns only the dividend (in integer), and mod returns the remainder. For example,

15 / 8 = 1.875 or 1 ... 7

15 div 8 = 1

15 mod 8 = 7

In the above example, we can see only real values between 0 and 8 (include 0, exclude 8) will return for anything mod 8. Back to our case, now 259 mod 256 = 3, and anything else on the scale mod 256 = anything else on the scale. So by editing the equation to the following the problem is solved.

G = (4G' + 7) mod 256

However the inverse form is more tricky. You cannot move the mod operator to the another side, but since it outputs in cycles, we can write another formula which create the same results. Just like sin x = sin (x + 2pi), we transform the inner function by one cycle into

G = 4G' - 249

G' = (G + 249) / 4

Because mod works not as our purpose when the inputs go across positive and negative; now I make G' always positive in the equation just above. Our G input here should be a number of the sequence 3, 7, 11, 15, ..., 251, 255, then the G' outputs would be integers from 63 to 126. To transform this range back to 0 to 63 correctly (64 to 0, 65 to 1, ... instead of 63 to 0, 64 to 1, ...), just mod the G' (which equals to [G + 249] / 4; putting "mod 64" to the left side does not work) is fine, so

G' = [(G + 249) / 4] mod 64

This concludes the first interpretation.

The another one sounds more complicated; breaking down G' into two parts, into a scale ranged from 0 to 31, and another one only gives 0 or 1, then sum up their results. The principle of this interpretation is to include the very first intended RECT mechanics, as my theory in Part 5, and separate out the later modification. Recall the binary representation of RECT discussed in Part 5 again to show this new division; the binary RECT is now divided in the way like 00000 00000 0 00000 -- the 6 bits of green is further apart to become a group of 5 bits and 1 separated bit. What special about this system, is if you ignore the isolated bit by assuming it is always "0", the another three groups of 5 bits each, work all the same. All of them are in 32-step scales, from degree 7 to 255 of the RGB scale. Of course, nothing are to be ignored finally; the isolated bit actually represents an addition of a degree 4 of green. When it is 0, nothing happens; when it is 1, G will increase by 4.

The 6-bit G' system records G' in a range from 0 to 63. Now, its first bit from the right is isolated to a 0-or-1 field, the remaining 5 bits are in a 0-to-31 scale. In terms of numerical value, the new 0-to-31 scale is actually a double of the previous 0-to-63 scale, since moving one binary digit means multiplying by 2. So, in terms of the previous scale, the new scale is listing from 0 to 62, in steps of 2, then all even numbers from 0 to 62 are available. Adding up the 0 or 1 of the remaining bit, odd numbers from 1 to 63 are now also available. Combining the two sequences, all integers from 0 to 63 are present.

If we notate the transforming equation under this interpretation, it will be

G = (8g' + 7 + 4g'') mod 256

4 (2g' + g'') = G - 7

2g' + g'' = (G - 7) / 4

2g' + g'' = [(G + 249) / 4] mod 64

(G + 1) must be divisible by 4.

Where g' and g'' are the depth values in the 0-to-31 and 0-or-1 new green scales respectively. Pay attention to the first equation; I put the variable g'' to the last, is for you to compare this equation with those of red and blue. You see basically only 4g'' is extra. And for the next equation, actually the previous paragraph is concluding 2g' + g'' = G'. For the last statement of condition, you see "-" becomes the "+" sign. Actually it could be represented as an equation too,

(G + 1) mod 4 = 0

When it is "mod 4", now you see it is fine to add or subtract anything in multiples of the divisible-by number. All the other conditions would be

(R + 1) mod 8 = 0; (R + 1) must be divisible by 8, under certain conditions.

(B + 1) mod 8 = 0; (B + 1) must be divisible by 8.

6.5
6.5 - R'G'B' to RGB

So far, all the separate exchanging formulas are listed, then it is time to transform them as a whole, since both RECT and (hexadecimal) RGB are 1-D systems. First, let's recall how they are made up of R', G', B', R, G and B.

RGB = 65536R + 256G + B = R (16^4) + G (16^2) + B

RECT = 2048R' + 32G' + B' = R' [2^(6 + 5)] + G' (2^5) + B'

Using all the equations we have made up above, this complicated looking task is very easy. Just substitude and get ...

RGB *= 65536 (8R' + 7) + 256 [(4G' + 7) mod 256] + (8B' + 7) ... (1)

RECT *= [2048 (R - 7) / 8] + 32 {[(G + 249) / 4] mod 64} + (B - 7) / 8 ... (2)

No, I did not simplify or underline them, because these are not the truth. And also look at this section's subtitle, equation (2) is not our business now. Let's focus on equation (1)... and besides, we still have not explained yet why there are 64 colors on red, but not 32 as intended. Remember the reason for adding "mod 256", is because the G output of G' exceeds 255, and we have to limit it into the correct color cycle. But actually, the exceeded amount is just transformed into the next digit; for the digits responsible for green, they works the same. Here we should actually remove the operator, and (1) is now

RGB = 65536 (8R' + 7) + 256 (4G' + 7) + (8B' + 7) ... (1a)

As in Part 3 discussed, computers get the three base colors by performing division to the RGB code. This equation has deliberately broken down into the wanted three parts; (8R' + 7) is R, etc. We are ready to discuss the phoneme in red. Suppose we have R'0 G'63 B'0 (which is RECT: 2016, tested and shown in the table of Part 5), substitute in (1a)

RGB
= 65536 (7) + 256 (259) + (7)
= [65536 (7) + 256 (256)] + 256 (3) + 7
= 65536 (8) + 256 (3) + 7
=
...

You see when the green value exceeds the limit, one cycle of green becomes degree one of red. This strange action is the same as we move point-by-point on the linear RGB or RECT system instead of cycle-by-cycle. The system itself runs this weirdly, because defining the color cycles into 1-D already strays away from normal concepts. When using, we makes it looks normal by avoiding certain moves, but they look nothing special to computers, since they just perform mathematical calculations. It is humans who define the outputs as colors. Anyways, now we see the extra red degrees are come from color cycle exchanges. The demonstration also shows that the advancement takes place in RGB calculation, but not in RECT, or else the red degree will increase by 8. So R', G' and B' values lie in their own range, not overlapping others, only green with red after transforming into RGB due to the strange representation of G by G' (G'63 is G3).

Remember the second interpretation of G'-to-G formula? It is to make all R', G' and B' work fine without "advancements", and transfer the responsibility to the isolated bit g''. Equation (1) under this interpretation would be

RGB = 65536 (8R' + 7) + 256 (8g' + 7 + 4g'') + (8B' + 7)

RGB = 65536 (8R' + 7) + 256 (8g' + 7) + 256 (4g'') + (8B' + 7)
... (1b)

(To recall, G' = 2g' + g'', where g' and g'' are integers ranged 0 to 31 and 0 to 1 respectively.)

Now, if g' is saturated (g' = 31, 8g' + 7 = 255), g' with a fourth of g'' would be part of R, otherwise it is to be combined with g''. For the same RECT: 2016, the example using (1b) would have an outcome of

RGB
= 65536 (7) + 256 (255) + 256 (4) + (7)
= 65536 (7) + 256 (255 + 1) + 256 (4 - 1) + 7
= 65536 (8) + 256 (3) + 7
=
...

I guess now we have explained everything concerning converting R'G'B' to RGB ..., except one, which leads me still do not underline the equations -- mod. What if R and the advancement from G combineded together? Refer to the Part 5 table, RECT: 65503 is not the upper limit for distinct outputs as YinYin said, but continues shortly until RECT: 65535. R' is 31 but R is 0 since RECT: 65504. In fact -- you should get it yourself by now -- the 0 is just a cycled degree 256, so we need a mod operator for RGB too. The following are modified equations (1a) and (1b), which should be correct.

RGB = [(16^4) (8R' + 7) + (16^2) (4G' + 7) + (8B' + 7)] mod (16^6) ... (A)

RGB = [(16^4) (8R' + 7) + (16^2) (8g' + 7) + (16^2) (4g'') + (8B' + 7)] mod (16^6) ... (B)

We can now simplify them, to be

RGB = (524288R' + 1024G' + 8B' + 460551) mod (16^6)

RGB = (524288R' + 2048g' + 1024g'' + 8B' + 460551) mod (16^6)

Finally, here comes to an end, for exchanging the systems in terms of their components. Take a break for the completion of the long Part 6, and ready for long-awaited two-way formulas.



7
Part 7 - RECT to RGB and its Inverse

The real application of all we have discussed, is to understand what RECT means (RECT to RGB), and learn how to represent colors in RECT (RGB to RECT). Base on the concepts we have gone through, we can now talk about the transforming equations.

7.1
7.1 - RECT to RGB

RECT is not the same as R'G'B', so RECT to RGB differs from R'G'B' to RGB as above discussed. But of course R'G'B' is only derived from RECT, so there must be some transforming methods. We have listed something related to it at the beginning of Part 6.5, and described it long before, at the last sentence of Part 4:

RECT = 2048R' + 32G' + B' ... (3)

This is the R'G'B' to RECT formula, while in Part 6.5 ending, we have the R'G'B' to RGB formula; now we ultimately seek the RECT to RGB formula. By a principle similar to the Chain Rule of differentiation, RECT to RGB can be obtained by considering RECT to R'G'B' to RGB. The second "to" can be achieved by simple substitutions, while the first "to" requires an inverse formula of R'G'B' to RECT. For R'G'B' to RECT, it is a 3-variable-to-1 formula, so its inverse requires 3 equations, and look like

R' = RECT ... ???

G' = RECT ... ???

B' = RECT ... ???

It is not that difficult, we have done similar calculations before. See the ending of Part 3, we were decoding RGB into R, G, B separately through division. The process involves dividends and remainders, so using div and mod (for their operations, see Part 6.4) are exactly the way we are looking for. Now R', G' and B' move in steps of 2048, 32 and 1 unit respectively, so we just have to count how many steps they have taken, and ignore the remainders. Our first draft would be

R' *= RECT div 2048

G' *= RECT div 32

B' *= RECT

"div 1" means nothing as RECT must be integer. We can surely say at once that this draft is incorrect, just by considering B'. It should runs in cycles of 32 instead. Remember we successfully represent cycles by using mod, so it is to be applied here too. Therefore,

B' = RECT mod 32

For G', RECT div 32 gives you how many complete steps of 32 units each it has taken. Its cycle is 64 units, so to bound it within the range, we mod the result, leading to

G' = RECT div 32 mod 64

Both operators have to same level of priority, so the calculation will be done from left to the right, and no brackets are needed. The R' equation is marked incorrect since we can input RECT values over 65535, as shown in the tables of Part 4 and 5. To bound it, we need the same operation as in G' and B', which makes it

R' = RECT div 2048 mod 32 = RECT div 64 div 32 mod 32

Finally, we now obtained the three inverse equations of R'G'B' to RECT. It is time to substitute them into R'G'B' to RGB. For (A) at the ending of Part 6.5, after substitution it will be

RGB = {(16^4) [8 (RECT div 2048 mod 32) + 7] + (16^2) [4 (RECT div 32 mod 64) + 7] + [8 (RECT mod 32) + 7]} mod (16^6)

Even with the simplified form, (B), it is still very long.

RGB = [524288 (RECT div 2048 mod 32) + 1024 (RECT div 32 mod 64) + 8 (RECT mod 32) + 460551] mod (16^6)

You cannot simplify the div and mod operations, or you will alter the equation. They are not simple divisions. This concludes RECT to RGB, which presenting the way computers process our RECT input in background files to RGB code for displaying. It directly exchanges the two system linearly.

7.2
7.2 - RGB to RECT

For RGB to RECT, it is even more complicated, because not all RGB values are permitted. In fact, as RGB is 24-bit and RECT is 16-bit, only one-256th of RGB colors are available on RECT. Therefore, very first we can imagine it is something like every 256 units on the RGB line there is 1 block permitted, and in fact they are not even equally spread "256 units a step". It is really hard to exchange one line to one line backwards as we just did in RECT to RGB, and a bit pointless.

It is pointless because we usually read R, G, B separately. Even if it uses the linear form, it is still hexadecimal so that we can read the three colors two digits by two digits. We use them separately also. Our formulas are likely to be written in decimal, but no one bothers to transform three color depths into one huge decimal number up to 16 million, before the exchanging process. Actually what worth our discussion is the exchange between RECT and the set of R, G and B. We use only these two system, but not R'G'B' or RGB in decimal. Therefore, we should focus on what we need, and derive formulas in terms of them.

Up to this moment, we are now separating the terms R, G, B and RGB. "R, G, B" means the three numbers are presented one by one, instead of like RGB a single number under a linear system. While for R'G'B' or "R', G', B'," it does not bother since for sticking up R', G' and B' as a single number, the system is named RECT, so we do not have to differentiate them. In summary,

Code:
References of Color Notating Systems

               Linear   3-D
               system   system

RGB-related    RGB      R, G, B

RECT-related   RECT     R'G'B'
                      or R', G', B' occasionally

7.3
7.3 - R, G, B to RECT

R, G, B to RECT means an equation with one side of RECT output and one side of R, G, B inputs, so it is a 3-variable-to-1 formula, similar to R', G', B' to RECT as (3) in Part 7.1.

RECT = 2048R' + 32G' + B' ... (3)

Remember we have equation (2) in Part 6.5 left to be discussed:

RECT *= [2048 (R - 7) / 8] + 32 {[(G + 249) / 4] mod 64} + (B - 7) / 8 ... (2)

It is marked incorrect because it is substituted by separate base color cycles, without considering the green-to-red advancement discussed in later parts of that section. How about substituting the form without mod rounding? That is

R' = (R - 7) / 8

G' = (G - 7) / 4

B' = (B - 7) / 8

Into RECT and becomes

RECT = 2048 (R - 7) / 8 + 32 (G - 7) / 4 + (B - 7) / 8

RECT = 256R + 8G + (B / 8) - 1848.875

It seems that the advancement of green to red is considered since here R will increase if G' is 63, but actually, it is wrong because the conservation in the two systems are not equal. If we notate the green part without rounding the cycle, as above, one cycle of G', means one point of R', not R, will be lost if G' is 63. Then, R', not R, needs to be increased by 1 as a contradict action; now it is only R the scale increased. R is a scale 8 times more detailed than R', so even if R is advanced, R' would only increase by an eighth point. It needs to be a whole point if we need the formula works. On the other side, if we round the cycle by adding mod operations as (2), we need other operations to assume 8 is 7, ..., 0 is 255 in red mathematically. That means

R' = (R - 8) / 8 when G = 3,

else R' = (R - 7) / 8

One approach to combine the two equations, assuming only permitted inputs (0, 7, 8, 15, ...) are received, is using trunc( ), a rounding down function. Then

R' = trunc[(R - 7) / 8]

The only problem is we need to assume 0 as 255 too, but if R = 0 here, R' will output 0 (= trunc -0.875). We could use addition by a cycle and mod here again:

R' = trunc[(R + 249) / 8] mod 32

Which can avoid the process takes place across 0.

While for G', for mod cycling it should be

G' = [(G + 249) / 4] mod 64

Therefore, finally

RECT = 2048 {trunc[(R + 249) / 8] mod 32} + 32 {[(G + 249) / 4] mod 64} + (B - 7) / 8

This looks complex and time-consuming when operated by human. So, if we adjust the special red and green values in advance, we can use the basic equation. The whole R, G, B to RECT process would be

If G = 3, conduct

1) assign G = 259, and
2) subtract R by 1 or assign R = 255 if R = 0 originally.

Then, apply RECT = 256R + 8G + (B - 7) / 8 - 1848

7.4
7.4 - RECT to R, G, B

After then, we should talk about the inverse formula again. This is very similar to RECT to RGB, just being broken down into three parts. And this is easy to be obtained since we have discussed RECT to R'G'B' and all those R' to R and alike. They are

R *= 8R' + 7; R' = RECT div 2048 mod 32 = RECT div 64 div 32 mod 32

G = (4G' + 7) mod 256
; G' = RECT div 32 mod 64

B = 8B' + 7
; B' = RECT mod 32

The final concern would be the R' to R formula, as R will increase by 1 if G' = 63, reason discussed in Part 6.5. It has been concluded that the advancement is due to an absence of rounding G by mod. So, we can similarly apply the mechanics into the R' to R formula, i.e.

R = 8R' + (4G' + 7) div 256 + 7

Then, when and only when G' = 63, (4G' + 7) will reach 256 and thus (4G' + 7) div 256 will be 1. Finally, after substitution, the whole set of RECT to R, G, B equations are now derived:

R = 8 (RECT div 2048 mod 32) + [4 (RECT div 32 mod 64) + 7] div 256 + 7

G = [4 (RECT div 32 mod 64) + 7] mod 256

B = 8 (RECT mod 32) + 7

All the two-way formulas are derived, but they seem to spread apart quite much and need summarizing. I will do this later in Part 9, but not at once, because ... the calculations are not always true. There are a few preset RECT to RGB sets, which used in the official stage backgrounds. Only after discussing them, I could then show you the whole RECT and R, G, B converting algorithm. But before moving on, we have to tackle one last issue on the calculations.

7.5
7.5 - Handling Negative RECT

Although it is rare, but there might be people who playing tricks -- what if the RECT input is negative? For computers, usually they handle the negative values by Two's Compliment, that is moving the whole set of negative to above the positive numbers. For example in a 16-bit Two's Compliment, -32,768 to -1 will be represented by binary numbers from 1000 0000 0000 0000 to 1111 1111 1111 1111, which is primarily 32,768 to 65,535 in decimal. It is possible since digital numbers must have a range, here it is -32,768 to 32,767, therefore it is not a problem to input negative values into RECT for the computer, it just read the number in the designed range. But in mathematics, both positive and negative signs tend to infinity, there is no representation to transfer a negative number to be a certain positive number within the range.

The only method to handle negative inputs with our formulas is to write a conditional statement which makes use of infinity. This should be done:

If RECT < 0, repeat adding 65536 to RECT, until RECT >= 0.

After this final issue regarding the formulas is settled, we can finally close the file, and move on the RECT-R, G, B conversion which have nothing do with the formulas. Sounds horrifying.



8
Part 8 - Exceptional Cases

It is mentioned at the very beginning of this entire article, the official maps that used RECT codes. It is mentioned for showing how common, useful or important to learn and use RECT, but actually only a small portion of these background's RECT follow all the formulas we have discussed! Obtained in LF2 ver 2.0a, At the first time I found this out, I thought I was screwed for experimenting:

Code:
rect  R   G   B   bg  Exceptional
----- --- --- --- --- ---
Code:
4705  23  83  15
4706  16  79  16  lf  Yes
4707  23  83  31
...
16834 71  63  23
16835 66  56  24  qi  Yes
16836 71  63  39
...
21095 87  83  63
21096 90  78  75  sp  Yes
21097 87  83  79
...
25356 103 103 103 hkc
...
29582 119 119 119 hkc
...
34815 136 3   255
34816 143 7   7   ft
34817 143 7   15
...
37769 151 119 79
37770 154 110 90  gw  Yes
37771 151 119 95
...
40179 159 163 159 hkc

These cases are not common, but definitely worth notifying. Those marked "Yes" in the last column are values do not obey the exchange formulas, while the "bg" column specifies which background these RECT values are used in. There are four special values, RECT: 4706, 16835, 21096 and 37770. It is believed that there is a process, prior to the general exchange formulas, that check whether the RECT is one of the special values -- if yes, output the corresponding special RGB codes -- instead of hidden instructions exist in the official backgrounds. The evidence is that, the output is still irregular even if we use one of these RECT values in custom backgrounds. All the RECT adjacent to the special values are normal.

The RECT codes remained the same throughout LF2 ver 1.9 to 2.0, however, in versions since 1.9c only 32-bit color is expected, while in versions 1.9 and before only so for 16-bit color. The RECT to RGB process back in ver 1.9 is different, and has different outputs:

Code:
--- ----- 16-bit mode 32-bit mode -- -- --
bg  rect  R   G   B   R   G   B   R' G' B'
--- ----- --- --- --- --- --- --- -- -- --
lf  4706  16  77  16  0   18  98  2  19 2
sp  21096 82  77  66  0   82  104 10 19 8
gw  37770 148 113 82  0   147 138 18 28 10
qi  16835 66  56  24  0   65  195 8  14 3
ft  34816 140 0   0   0   136 0   17 0  0
hkc 40179 156 158 156 0   156 243 19 39 19

Shortly regarding the 32-bit mode -- running LF2 ver 1.9 in 32-bit color -- now you see why there were greenish and bluish areas in the old version backgrounds. Try putting R, G, B into the to-RGB formula (first formula in Part 6.5). For the normal range of RECT of 0 to 65535, red depth here will never change from 0.

Out of those in the 16-bit mode columns, Queen's Island has the perfect color match between the graphics and RECT-coded area, while Stanley Prison and The Great Wall match poorly; Forbidden Tower does not care since there is no need of matching. Compare the two tables in this section, we see the RGB values of the RECT-coded area of Queen's Island in the two versions are exactly the same, while in the poorly matched backgrounds there are nearly 20 points of difference in total, for each of them. None of the background .dat or .bmp files were edited, but the color matching was fixed. It maybe the easiest and most perfect method to match colors by adding special conditions into the compiling process from RECT to RGB. Now we see the RECT-coded areas match very well to the graphics in 32-bit versions, thanks to this edition. Remember there is strict limit of color availability under normal RECT to RGB formula, and all the four "perfect matching" colors are not available there. For the background does not require color matching, Forbidden Tower, there is no special color adjustment set for it as its RGB output follows the formula.

In summary, RECT-coded colors in four of the official backgrounds are optimized to match other graphics by modifying their RGB output, instead of re-coding the whole RECT to RGB mechanism and editing the background files. This could explain why there are a few RGB outputs are out-of-pattern. This leads to a special use of the four RECT values, and at the same time the shift of the four sets of permitted RGB: R16 G79 B16, R66 G56 B24, R90 G78 B75 and R154 G110 B90 are made available and can be directly link to the special RECT values; R23 G83 B23, R71 G63 B31, R87 G83 B71 and R151 G119 B87 are made unavailable dispute the other availability conditions.



9
Part 9 - Mechanical Summary and Applications

Every detailed cases regarding exchanging RECT and RGB have been mentioned so far. They should cover all the conditions we will face while exchanging the two systems. A lot of terms and converting formulas has been discussed and developed throughout this article, one by one about how they are derived. This section will summarize them by going through all the results once in an organized way, for an easier understanding of their usage.

9.1
9.1 - Overview of Formulas

First let's see what important component and final formulas we have derived. There are several categories based on both sides of the exchanging systems. The four systems we have made use of are (1) R, G, B, (2) RGB, (3) R'G'B', and (4) RECT.

- - Rules and system exchanges - -
  • R, G, B to RGB (Part 3, 6.5)
    • RGB = 65536R + 256G + B
    • Condition: 0 <= (R or G or B) < 256
  • R'G'B' and replacement (defined in Part 4, 6.4)
    • Condition: R', G', B', g' and g'' are integers.
    • Condition: 0 <= (R' or g' or B') < 32
    • Condition: 0 <= G' < 64
    • Condition: 0 <= g'' < 2
    • Component: G' = 2g' + g''
  • R'G'B' to RECT (Part 4, 6.4, 6.5)
    • RECT = 2048R' + 32G' + B'
    • RECT = 2048R' + 64g' + 32g'' + B'
  • RECT to R'G'B' (Part 7.1)
    • R' = RECT div 2048 mod 32
    • G' = RECT div 32 mod 64
    • B' = RECT mod 32
  • R'G'B' to R, G, B
    • R = 8R' + (4G' + 7) div 256 + 7 (Part 6.3, 7.4)
    • G = (4G' + 7) mod 256 (Part 6.4)
    • B = 8B' + 7 (Part 6.2)
  • R, G, B available in RECT
    • Condition: (R, G, B) are one of the four sets, which can be linked to RECT output directly: (16, 79, 16), (66, 56, 24), (90, 78, 75) or (154, 110, 90). (Part 8)
    • Condition: (R, G, B) are not either one of the four sets: (23, 83, 23), (71, 63, 31), (87, 83, 71) or (151, 119, 87). (Part 8)
    • Condition: When G <> 3, (R + 1) must be divisible by 8. (Part 6.3)
    • Condition: When G = 3, R must be divisible by 8. (Part 7.3)
    • Condition: (G + 1) must be divisible by 4. (Part 6.4)
    • Condition: (B + 1) must be divisible by 8. (Part 6.2, 6.4)
    • (If the first condition is met, the remaining are to be ignored; otherwise, check the remaining, and if either one of them is not met, the input will be rejected.)
  • R, G, B to R'G'B'
    • R' = trunc[(R + 249) / 8] mod 32 (Part 6.3, 7.3)
    • G' = [(G + 249) / 4] mod 64 (Part 6.4)
    • B' = (B - 7) / 8 (Part 6.2)
  • RECT to R, G, B (Part 7.4)
    • R = 8 (RECT div 2048 mod 32) + [4 (RECT div 32 mod 64) + 7] div 256 + 7
    • G = [4 (RECT div 32 mod 64) + 7] mod 256
    • B = 8 (RECT mod 32) + 7
  • R, G, B to RECT (Part 7.3)
    • For all cases: RECT = 2048 {trunc[(R + 249) / 8] mod 32} + 32 {[(G + 249) / 4] mod 64} + (B - 7) / 8
    • When G <> 3: RECT = 256R + 8G + (B - 7) / 8 - 1848
  • Negative RECT to positive (Part 7.5)
    • If RECT < 0, repeat adding 65536 to RECT, until RECT >= 0.
  • Special RECT values (Part 8), assignments prior to all equations
    • When RECT div 65536 = 4706, (R, G, B) = (16, 79, 16). When reversed, RECT = 4706.
    • When RECT div 65536 = 16835, (R, G, B) = (66, 56, 24). When reversed, RECT = 16835.
    • When RECT div 65536 = 21096, (R, G, B) = (90, 78, 75). When reversed, RECT = 21096.
    • When RECT div 65536 = 37770, (R, G, B) = (154, 110, 90). When reversed, RECT = 37770.
9.2
9.2 - The Exchange Cycle

We have to spot on the final formulas out of these and make use of them. For the frame of the sequence of using the formulas, remember our purpose here, is "to understand what RECT means (RECT to RGB), and learn how to represent colors in RECT (RGB to RECT)" (Part 7). As we are not the original developers of LF2, everything of it were existed before our exploration. Therefore, we start from interpreting them, RECT to RGB, and be able to convert them back, RGB to RECT; the linked ends form the Exchange Cycle. Let's take a deeper look to this draft.

As discussed in Part 7.2, "RGB" is actually "R, G, B" since we do not use them as computers do -- a reminder. Breaking down the process from RECT to RGB; it would look like

Exchange Cycle Algorithm ... Wrote:Read RECT -> Break to R'G'B' -> Convert R'G'B' to R, G, B individually -> Get R, G, B

Under the standard calculations. However, we missed some the special cases which need to be checked beforehand: (1) the negative and (2) the four special RECT values. (1) should be prior to (2) since the conditions of (2) do not cover across zero. The whole flow is now drafted as

Exchange Cycle Algorithm ... Wrote:Read RECT -> Put it back to positive (if needed) -> Check if: It is one of the four special values

If: It is -> Check what RGB it refers to ->
If: It is not -> Break RECT to R'G'B' -> Convert R'G'B' to R, G, B individually ->

-> Get R, G, B

This is a more detailed version. Actually we can make use of some final equations to simplify it and get "Read RECT -> Convert to R, G, B -> Get R, G, B" by using RECT to R, G, B formulas (Part 7.4).

The another half of the cycle is converting some RGB we want to RECT, which should be probably more useful. Let's discuss the steps one by one. Starting off on RGB, we first need to check if it is permitted. This can be done by applying the condition checks listed in "R, G, B available in RECT" of Part 9.1. The first condition is special for handling the optimized colors (Part 8); if it is met, RECT result is got. For the remaining, the order of applying them does not bother much, since if either one of them does not satisfied, the whole RGB set will be rejected. The draft for this would be

Exchange Cycle Algorithm ... Wrote:Input and read R, G, B -> Check if: (R, G, B) is one of the four sets: (16, 79, 16), (66, 56, 24), (90, 78, 75) or (154, 110, 90)

If: It is one of them -> Check what RECT it refers to ->
If: It is not -> Check if: G <> 3

If: It is (G <> 3) -> Check if: (G + 1) is divisible by 4
--- If: It is not -> Reject the input
--- If: It is divisible -> Check if: (R + 1) is divisible by 8 --(Move on)-->
If: It is not (G = 3) -> Check if: R is divisible by 8 --(Move on)-->

If: It is not -> Reject the input
If: It is divisible -> Check if: (B + 1) is divisible by 8

If: It is not -> Reject the input
If: It is divisible -> Check if: (R, G, B) is one of the four sets: (23, 83, 23), (71, 63, 31), (87, 83, 71) or (151, 119, 87)

If: It is one of them -> Reject the input
If: It is not -> (Calculate R, G, B to RECT) ->

-> Get RECT

Looks complicated in text. Besides this, we can complete the cycle after filling up the calculation process. Similar to RECT to RGB, there are two algorithms, one with one step more, for being exchanging to R'G'B', finally then to RECT, instead of directly to RECT. The longer algorithm here is

Exchange Cycle Algorithm ... Wrote:(If: The color is permitted) -> Convert R, G, B to R'G'B' individually -> Combine R'G'B' into RECT -> Get RECT

For the shorter algorithm, this time it is not that simple since there are two alternatives. It can be that simple if we take the general formula, but it is a bit complex for manual calculations and there is replacement for most cases, so an alternative is developed.

Alternative 1: Exchange Cycle Algorithm ... Wrote:(If: The color is permitted) -> Apply the general R, G, B to RECT formula -> Get RECT
Alternative 2: Exchange Cycle Algorithm ... Wrote:(If: The color is permitted) -> Check if: G = 3

If: Yes -> Assign G = 259 -> Check if: R = 0
--- If: Yes -> Assign R = 255 ->
--- If: No -> Subtract R by 1 ->
If: No ->

-> Apply the R, G, B to RECT formula for G <> 3 -> Get RECT

The cycle is now completed, in text or algorithms or pseudo-codes or whatever. It is quite hard to read and confuses us, maybe a graphical representation would be better. Here I present the only picture of this article:

[Image: IMG_0003.jpg]
(click for zoom in options)

It is presented in a flowchart. Basically, rounded rectangles represent starting and ending points, parallelograms represent processes, and rhombuses represent conditional checks. Cases are one type of conditional check with multiple possibility, while normal checks only have "yes" or "no" -- "TRUE" or "FALSE" results. The "←" sign represents assignments, a process mentioned in Part 7.3, which set the value of the left-hand-side variable to the result of the right-hand-side. The symbol "=" with a "←" above means the same as "←" assignment, just to retain the equal sign since we have used it in all our equations discussed. By following this flowchart properly, you should be able to perform the necessary calculations.

9.3
9.3 - Formulas for Secondary Base Colors

At least, I have heard people asking for RECT values for perfect grey. From this, I also think about deriving perfect secondary base colors such as yellow (red + green), aqua (green + blue) and purple (red + blue). It is not complicated, only to assume two colors have the same depth, and one is always zero; or all colors have the same for grey. The only trick is to use g' instead of G'. Recall the R'g'B' to RECT formula,

RECT = 2048R' + 64g' + 32g'' + B'

For yellow, let it be Y', and Y' = R' = g', g'' = B' = 0. Formula:

RECT = 2112Y'

For aqua, let it be A', and A' = g' = B', R' = g'' = 0. Formula:

RECT = 65A'

For purple, let it be P', and P' = R' = B', g' = g'' = 0. Formula:

RECT = 2049P'

For grey, let it be Gr', and Gr' = R' = g' = B', g'' = 0. Formula:

RECT = 2113Gr'

Y', A', P' and Gr' are all integers in a scale ranged 0 to 31, as R', G' and B' do. Regarding the Y' to Y (in RGB scale), A' to A, etc. formulas, they are the same as the B' to B formula (Part 6.2),

B = 8B' + 7

With only a replacement of variable names.



10
Summary

This short passage summarizes our discussion so far.

RECT is a color code used in LF2 background files, uses integers ranged 0 to 65535 to represent three base colors linearly. The base colors are red, green and blue; there are 32, 64 and 32 distinct depths respectively available in RECT representation (Part 4). Representing colors linearly means combining the three variables into one, works like hexadecimal RGB (Part 3) or LF2 stage id: (Part 2). Component variables are stored in cycles. By performing division on RECT and consider the dividends and remainders, cycles and individual color depths can be "decrypted". By converting RECT into binary is also a possible method since the component ranges are in powers of 2 (Part 5). Due to the 32- and 64-depths sets nature, real RGB values jumps in 8 or in 4 as RECT changes (Part 6). Because of a need of pure white and system optimization in developing RECT codes, (R, G, B) starts from (7, 7, 7) as RECT starts from 0, and green ends a cycle at 3 in RGB scale weirdly as the green component of RECT reachs its peak (Part 6.1).

For the RECT and RGB exchanging formulas, they are discussed step by step. First from a derived system of R'G'B', from RECT (Part 5), lead to possibly discuss R' and R, G' and G, and B' and B exchanges (Part 6), then re-combined for exchanging RECT and R, G, B (Part 7), finally some special preset RECT to RGB linkages (Part 8). During these parts we gradually translate texts and ideas into mathematical equations, and repeatedly fixing them by considering wider possibilities of inputs. Towards the end, we are focusing on the systems RECT, and R, G, B separately as they are the two we data changers would use. To summarize all the mathematical discussions, all rules and formulas are listed (Part 9.1) and the Exchange Cycle has been drawn for the following of manually exchanging the two system (Part 9.2). Last but not least, at the very last part some other basic colors are also considered into deriving to-RECT formulas: perfect yellow, aqua, purple and grey (Part 9.3).

With completing the entire discussion, we now can understand how RECT works and is recorded, then make use of our understanding of the whole process to create our own backgrounds, or fix others'. However, due to the limitation of RECT, and digital storage is decreasingly being an issue nowadays, it is not a serious problem to use .bmp to replace the RECT-coded areas. Still, understanding one of the long-recognized more advanced coding systems in LF2 data changing in such extent, could be a nice satisfaction.
Reply
Thanks given by: A-Man , YinYin , mfc , zort , empirefantasy , Som1Lse , Dr. Time
#2
hello Ikran. Nice article, but I have to say it is a little too long. Sorry if I am missing the points of your article, but if you are trying to have a formulation to convert rect numbers to RGB colors, here is a fairly accurate routine I used in F.LF background implementation. The rect code in LF2 is not much a mystery. I am pretty sure it is a RGB 5:5:5 format, each color taking 5bits, the 1bit remaining has special effect.
Code:
var lookup, computed;
switch (rect)
{
    case 4706: lookup='rgb(16,79,16)'; break; //lion forest
    case 40179: lookup='rgb(159,163,159)'; break; //HK Coliseum
    case 29582: lookup='rgb(119,119,119)'; break;
    case 37773: lookup='rgb(151,119,111)'; break;
    case 33580: lookup='rgb(135,107,103)'; break;
    case 25356: lookup='rgb(103,103,103)'; break;
    case 21096: lookup='rgb(90,78,75)'; break; //Stanley Prison
    case 37770: lookup='rgb(154,110,90)'; break; //The Great Wall
    case 16835: lookup='rgb(66,56,24)'; break; //Queen's Island
    case 34816: lookup='rgb(143,7,7)'; break; //Forbidden Tower
}
var r = (rect>>11<<3),
    g = (rect>>6&31)<<3,
    b = ((rect&31)<<3);
computed = 'rgb('+
    (r+(r>64||r===0?7:0))+','+
    (g+(g>64||g===0?7:0)+((rect>>5&1)&&g>80?4:0))+','+
    (b+(b>64||b===0?7:0))+
    ')';
if( lookup && computed!==lookup)
    console.log('error! computed:'+computed,'correct:'+lookup);
if( lookup)
    return lookup;
else
    return computed;
basically bit 1-5 is for B, bit 7-11 is for G, bit 12-16 is for R. thse bits make the 5 most significant bits of the 8-bit channel value. the 3 least significant bits are either 111 or 000. for example 33580, binary is 10000 01100 1 01100, take 10000 and pad 111 gives 10000111 which is 135, 01100111 is 103. which converts 33580 into rgb(135,107,103). note, the G channel value is 103+4=107, which I suspect is due to the magic bit. Anyway, it is possible that LF2 keeps a color table internally, then there may not be a general mathematical formula for this conversion process.

cheers
Reply
Thanks given by: Ikran Ahiyìk , Silverthorn , STM1993
#3
Hello tyt2y3, thanks for your concern and revealing the codes here. You see I'm no clue of such programming, and only derive the whole theory by experimenting and guessing. I see you are talking about converting rect to RGB; that's part of I did here, as we should first understand what rect means. But I also derived how to change back RGB to rect. To me this is a bit more useful, and it is actually the ultimate goal for I first went into this topic. Of course, you know the backwards conversion is just derived from rect to RGB, they are similar, but still there is a need for someone to evaluate it before everyone since not everybody could done it by themselves. Or else, much shorter posts could suffice.

Anyways somehow I was spending time to try to write out some ideas I've thought of. You're right it is indeed too long, even for me sometimes it is a bit difficult to chase one point that I had written there... One would be the binary rect as you're saying here. For the most of the article I divide RGB to 5:6:5, in respect to the color they are responsible for. But I did discussed the 5:5:5 plus 1 bit interpretation, somewhere gone through 70% of Part 6.4. About "LF2 keeps a color table internally," I guess you mean the switch in your code, I acknowledge it also and mentioned in Part 8, and it affects the rect to RGB conversion as you listed, also the available actual RGB in rect representation. What I'm aware of is that, not sure if rect: 4706, 16835, 21096 and 37770 are the only 4 values with RGB differ from the calculated one (the others are the same). Indeed one (set) of general formulas wouldn't work for the whole conversion process, they are only a part of it, together with some conditional checks.

Since 10 years this is the first time I discuss LF2 with others regarding something other than (amateur) gameplay. Thanks to you all, this place is wonderful.
Reply
Thanks given by:
#4
Hi Ikran, I am impressed by the systematic approach you took in your article. It is fun to read rect code color conversion like rocket science. I agree with you, a RGB to rect formula is more meaning to data changers. But it will be useful only if we have an implementation of your color conversion algorithm. If you are familiar with programming, it will be great to have written the algorithm in program code rather than pseudo code. In that way we can use it and test it. If you need help in programming, you can ask me or several other LF empire folks. We have great programmers here.
thank you for your long hard work, and I am looking forward to your other discussions of LF2 internals.
Reply
Thanks given by:
#5
Jolly gosh, I knew there were a few inaccuracies inside the first description I wrote up and I fixed quite a few inside the RTP tool you cannot seem to run. But I never expected anyone to possibly turn this into such a huge hassle. It looks like you had fun though.

Care to create another tool that simply works using your formulas, back and forth?

(01-01-2014, 03:53 AM)tyt2y3 Wrote:  hello Ikran.
(01-01-2014, 05:36 AM)Ikran Ahiyìk Wrote:  Hello tyt2y3, ...
(01-01-2014, 06:03 AM)tyt2y3 Wrote:  Hi Ikran, ...
Why so formal?
Reply
Thanks given by:
#6
Sorry if there's really some sort of offense... Maybe I was excited to figured out something I never understood before, and suddenly came up with a sequence of discussions bridging up to that, then, cared to post it.

I can't write any tools; and as you say it is fixed, I guess there wouldn't be any significant difference even if I do write one. It's very amazing to me for writing those tools, you all are geniuses...

(01-01-2014, 12:20 PM)YinYin Wrote:  Why so formal?
For me, I did what he had done to me; for him, I guess because I'm new...?
Reply
Thanks given by:
#7
Haha, there is nothing offending about it. I don't think I ever figured out those small twists and details you managed to find on it.
I bet there still are some conversion mistakes on my tool and I usually iron them out by converting back and forth as I use it.
Reply
Thanks given by:




Users browsing this thread: 1 Guest(s)