DEMONSTRATIVE EXAMPLES
======================
EXAMPLES ON IMAGING
===================
(This section requires version 1.55 or higher)
COLOR EDITING:
==============
Before learning more about colors, you need to review the "Setting Color and Font" section
of the "Personal C Sharp Reference for Desktop applications" and learn the 4 means of
setting the color.
You should now know that you can select a color by specifying the 4 components which make
the color. They represent "red, green, blue and opacity" in sequence and the value of each
component can be between 0:255. You load the 4 components into array CLI[] rows and make
the assignments (cls="c") before doing an operation which requires selecting color.
You need to know two more items:
(1) No matter which of the 4 means you have used to set the color, your data is supplied to
an internal method which processes the data and outputs the following:
a) The Color object referenced by (clp) if your data was for a single color or the two
objects (clp) and (clo) if your data was for two colors.
b) The array CLI[] loaded with the components of the color(s). This means that CLI[]
is always available and always contains the components of the last color set
regardless to whether you have used it to spaecify your color or not.
(2) If you like to obtain the color object(s) and the components of a specific color code,
you can do one of the following:
a) Do any operation which requires setting color like displaying text using method tm()
or setting paint brush using method gm("sps")
b) Call method gm("O") to obtain references to all objects which are used in Graphics.
Before this method supplies you with (clp) and (clo) it uses the value of (cls) which
you supply or the last value of (cls) available if you don't to obtain them for you.
Editing Color Pixel by Pixel:
=============================
The simplest way to edit image colors would be by scanning all pixels, reading each pixel color
and changing it as necessary. The problem with this method is in being slow. Other color
editing methods like using Image Attributes or Color Matrix work much faster. However, the
simple method is still the best option in some situations.
Method gm() at modes "bcg" and "bcs" are used to get and set a pixel color respecively. The
color array CLI[] is used for both modes to define the 4 components of a pixel color. The
pixel's horizontal and vertical locations relative to image's center are supplied to the method
assigned to (jf,kf)
===========================================================================================
Example 1: The image "icon.jpg" which comes with Personal C Sharp package is made of a gray
scale text in a pure blue background. We want to scan that image pixel by pixel and change
the color of all the pixels which make the background from blue to red. The pixels which
make the text should stay unaffected.
===========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
fls="images\\icon.jpg";gm("blf"); // Create a new (bip) and load the file into it.
jf=-200;gm("br"); // Draw the image at the left.
w=bip.Width/2;h=bip.Height/2; // w,h=half the width and half the height.
for (y=-h+1;y< h;y++) { // Scan image vertically
for (x=-w+1;x< w;x++) { // and horizontally
jf=x;kf=y;gm("bcg"); // Get each pixel's color data
if (CLI[0]<100 && CLI[1]<100 && CLI[2]>200) {
// If the pixel has high blue and low R,G
jf=x;kf=y;CLI=new int[]{255,0,0,255};gm("bcs");
} // Change its color to red.
}
}
jf=200;gm("br"); // Draw the image again at the right.
}
}
===========================================================================================
The most important part of the code is the condition stated to identify background pixels.
As we have mentioned before, the background has been originally painted in pure blue. So why
can't we use the conditional statement:
if (CLI[2]==255) {...}
or
if (CLI[0]==0 && CLI[1]==0) {...}
The problem was caused by "Compression". The image was saved into a "jpeg" file format causing
data to be compressed. When the image was read back and returned to its Bitmap form, it did
not return to exactly what it was.
The background still looks blue, but if you analyze the color components of its pixels, you'll
discover that some have a blue component of as low as 220 and some have red and green components
as high as 70 each. This is why we have found that we get the best results when we change the
conditional statement to:
if (CLI[0]<100 && CLI[1]<100 && CLI[2]>200) {...}

The Image Attributes:
=====================
The Image attributes are used for color editing. You can set these attributes using gm("sc")
Then, you can modify the present bitmap object (bip)'s color accordingly by calling gm("ba")
Method gm("sc") requires that you supply a character code indicating the type of the
attribute which you like to set assigned to (oc) before your call. You should also supply the
parameter(s) necessary for setting the attribute. Here is a list of the attributes and their
identity codes:
m : Color mapping. c : Color clearing.
g : Gamma Correction. t : Color threshold.
o : Output channel. x : Color matrix.
w : Wrap mode for Texeture brush.
COLOR MAPPING ATTRIBUTE:
------------------------
Used to Replace one or more of the colors found within the image with different one(s).
You can set color mapping attributes using more than one trip to the method.
In each trip you supply the original color and the color to replace it with. the two colors
are supplied to the method using the color components array CLI[] The first 4 rows of that
array are for the old color and the other 4 are for the new color.
COLOR CLEARING ATTRIBUTE:
-------------------------
You supply the method with a lower and upper limits for each color component. If the value
of any color component of a pixel was found to lie within the specified range, the pixel will
be made transparent.
The color limits are also assigned to CLI[]. Rows (0:2) are for the lower limits and rows (4:6)
are for the upper limits.
GAMMA ATTRIBUTE:
----------------
Gamma is the color contrast. It can be in the range (0.1:5) and is assigned to (jf)
COLOR THRESHOLD ATTRIBUTES:
---------------------------
The threshold is a value from 0 through 1 that specifies a cutoff point for each color
component. For example, suppose the threshold is set to 0.7, and suppose you are rendering
a color whose red, green, and blue components are 230, 50, and 220, respectively. The red
component (230) is greater than 0.7x255, so the red component will be changed to 255
(full intensity). The green component (50) is less than 0.7x255, so the green component will be
changed to 0. The blue component (220) is greater than 0.7x255, so the blue component will be
changed to 255.
You supply the threshold value assigned to (jf)
OUTPUT CHANNEL ATTRIBUTE:
-------------------------
When this attribute is set to one CMYK channel, Color is converted to CMYK and the intensity
of that channel is calculated for all pixels of the image. When the attribute is applied, the
image is redrawn in gray scale using the calculated channel intensity for each pixel.
You supply the channel name code assigned to (js) It can be "c", "y", "m" or "k" which means
"Cyan", "Yellow", "Magenta" or "Black" respectively.
You may specify a Color Profile file name to be used in the calculation of the channel
intensity. The path should be assigned to (fls) You may use the file name alone if it was
located into the folder:
%WINDIR%\system32\spool\color
COLOR MATRIX ATTRIBUTE:
-----------------------
You can do almost any kind of image color modification using the color matrix. To set this
attribute, you supply matrix elements assigned to the 5X5 array of float type CMF[][].
The Color Matrix will be discussed in more details later.
WRAP MODE FOR IMAGE TILING:
---------------------------
Method gm("spt") which is used to create a texture brush to tile drawn objects with uses this
attribute to know how to tile the object with the image assigned to it.
To set this attribute you assign to (js) one of the 5 single character codes:
c : Clamp. Means Paint one image only on the object.
t : Tile the object with as many images as it takes,
x : Reverse tiling order horizontally.
y : Reverse tiling order vertically.
b : Reverse tiling order both horizontally and vertically.
===========================================================================================
Example 2: Show the effect of applying different attributes to an image.
===========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
//---------------------- Create new (bip), Draw test pattern on it ---------------------
lf=110;of=60;gm("bn");gm("sdb"); // Create 110 X 60 (bip), set as graph. device
cls="b0";gm("sps"); // Create solid paint brush of pure blue
lf=100;of=50;gm("crf"); // Draw-fill a rectangle
gm("sdd"); // Switch graphical out device back to (bio)
Bitmap bt=bip; // Save (bip) temporarely
jf=-300;kf=150;gm("br"); // Render (bip) as is
//--------------------------- Show the effect of Color mapping -------------------------
bip=bt;gm("scn"); // Reset old attributes, prepare for new ones
CLI=new int[]{0,0,255,255,255,0,0,255}; // Old color=blue New Color=red
oc='m';gm("sc"); // Set Color mapping attribute
gm("ba");jf=-100;kf=150;gm("br"); // Apply attributes to (bip) then draw it
//------------------- Create new (bip), Load icon.bmp file into it. --------------------
fls="images\\icon.bmp";gm("blf"); // Create a new bitmap object and load file
bt=bip; // into it. Save (bip) temporarely
jf=100;kf=150;gm("br"); // Render (bip) as is
//---------- Show the effect of different attribute setups on resulting image ----------
bip=bt;gm("scn"); // Reset old attributes, prepare for new ones
oc='t';jf=0.7f;gm("sc"); // Set color threshold at 0.75
gm("ba");jf=300;kf=150;gm("br"); // Apply attributes to (bip) then draw it
bip=bt;gm("scn");
oc='g';jf=0.1f;gm("sc"); // Set Gamma at 0.1
gm("ba");jf=-300;kf=25;gm("br");
bip=bt;gm("scn");
oc='g';jf=1;gm("sc"); // Set Gamma at 1
gm("ba");jf=-100;kf=25;gm("br");
bip=bt;gm("scn");
oc='g';jf=2;gm("sc"); // Set Gamma at 2
gm("ba");jf=100;kf=25;gm("br");
bip=bt;gm("scn");
CLI=new int[]{0,0,0,0,255,0,255,0}; // Color clearing ranges: r=0:255, g=0:0,
oc='c';gm("sc"); // b=0:255 Set color clearing attribute
gm("ba");jf=300;kf=25;gm("br");
bip=bt;gm("scn");
oc='o';js="c";gm("sc"); // Set attributes for cyan output ch inspection
gm("ba");jf=-300;kf=-100;gm("br");
bip=bt;gm("scn");
oc='o';js="m";gm("sc"); // Set attributes for cyan output ch inspection
gm("ba");jf=-100;kf=-100;gm("br");
bip=bt;gm("scn");
oc='o';js="y";gm("sc"); // Set attributes for cyan output ch inspection
gm("ba");jf=100;kf=-100;gm("br");
bip=bt;gm("scn");
oc='o';js="k";gm("sc"); // Set attributes for cyan output ch inspection
gm("ba");jf=300;kf=-100;gm("br");
//---------------------------------- Text Display -------------------------------------
fns="crb16"; // Set font
kf=110; os=" Original Color Mapping Original Threshold attr.";
gm("ctf"); // Display first line
kf=-15; os=" Gamma=0.1 Gamma=1 Gamma=2 Color Clearing ";
gm("ctf"); // Display second line
kf=-140;os=" Output Channel Output Channel Output Channel Output Channel";
gm("ctf"); // Display third line
kf=-160;os=" Cyan Magenta Yellow Black ";
gm("ctf"); // Display 4th line
}
}

============================================================================================
WORKING WITH MATRICES:
======================
Matrices are used in the ".NET" mainly for two purposes:
(1) The Affine transform used for object positioning.
(2) The Color Matrix used for color editing.
We have made your setup of positioning matrix simple and mostly error free by letting you
supply the required parameters while we do the entire job for you. Since the order of applying
the transformation components can be a cause for errors, we use only one order which is:
Scale - Shear - Rotate - Translate
We let you base your setup on the PC# method of positioning which defines object locations
by the location of their centers relative to form's center.
For the color matrix, things are different. Using the color matrix to edit image colors is
the job of a professional who likes to handle the details of his job by himself. So we'll let
you calculate by yourself the matrix elements necessary for the color transformation job.
Let us have a very brief study of matrices to see how this job can be done. Although you have
no need to learn about the object positioning matrix, We are going to start with it in this
study since it is simpler. PC#'s transformation order and positioning will not be considered
in this study.
The object postioning matrix:
-----------------------------
If we like to move point (x,y) to a new position (x1,y1) and try to find a general formula
to be used to calculate the new coordinates based on all the variables involved while assuming
linear relationships, the formula would be:
x1 = ax + by + c and y1 = dx + ey + f
where a,b,c,d,e and f are constants.
The constant (a) which represents the new x-position as a function of the old (x-position)
is called the x-scaling factor. Similarly, (e) is the y-scaling factor.
The constant (b) which represents the new x-position as a function of the old (y-position)
is called the x-shearing factor. Similarly, (d) is the y-shearing factor.
The constants (c) and (f) are called the x-translation factor and the y-translation factor
respectively. We can now rewrite the formulae above as follows:
x1 = xScale.x + xShear.y + xTrans and y1 = yShear.x + yScale.y + yTrans
Mathematicians have found a more convenient way to represent these formulae and many other
similar ones using what they called "matrix". They also set rules for how to apply arithmatic
operations like addition and multiplication to matrices.
In our case, the old and new point positions can be expressed as the two matrices:
[x y 1] and [x1 y1 1]
respectively and the 2 formulae above can be expressed as:
[xScale yShear 0]
[x1 y1 1] = [x y 1] . [xShear yScale 0]
[xTrans yTrans 1]
Applying the rules set for multiplication of matrices, the formula above leads to:
[x1 y1 1] = [x*xScale + y*xShear +1*xTrans x*yShear + y*yScale + 1*yTrans 1]
and this is exactly what our original two formulae say.
According to matrices math, we can make a seperate matrix for each of the three operations
(scaling, Shearing, and translation) as follows:
Scaling: [xScale 0 0] Shearing: [ 1 yShear 0] Translating: [ 1 0 0]
[ 0 yScale 0] [xShear 1 0] [ 0 1 0]
[ 0 0 1] [ 0 0 1] [xTrans yTrans 1]
Then the matrices can be multiplied together in order to come up with the a matrix which
represents the combined operations. The order into which the multiplication takes place is
important. This is because a change in the order of applying the three operations to relocate
point (x,y) can change the result.
In order to simplify forming the 3 matrices above, start with the identity matrix which is:
[ 1 0 0 ]
[ 0 1 0 ]
[ 0 0 1 ]
The identity matrix does no transformation since it scales the object 1:1, adds no shearing
components and moves the object in both directions by zero pixels.
Rotation creates its own scale and shear components for both x and y. Rotation by the
angle (a) can be represented by the matrix:
[ cos a sin a 0]
[-sin a cos a 0]
[ 0 0 1]
Now you have 4 matrices representing scaling, shearing, rotatation and translation.
To obtain the overall positioning matrix you multiply the four together in the order at
which you like the transformation to take place.
Multiplication of matrices:
---------------------------
A matrix element is defined by its (row,column) Order numbers of rows and columns start by
zeros. A matrix is defined by its (Number of rows, Number of columns) like A(1,3) You can
multiply two matrices together if the number of columns of the first one matchs the number
of rows of the second one.
so you can multiply A(2,4) X B(4,3) and the result will be C(2,3)
Element c(x,y) of the resulting matrix (C) can be obtained by multiplying each element at the
(xth) row of (A) by the element at the (y)th column of (B) which matches it in order then
adding all resulting values together.
The Color Matrix:
-----------------
The color matrix is similar except that it is a 5X5 matrix in the form:
[rScale grShear brShear arShear 0]
[rgShear gScale bgShear agShear 0]
[rbShear gbShear bScale abShear 0]
[raShear gaShear baShear aScale 0]
[rTrans gTrans bTrans aTrans 1]
To transform a color with the red, green, blue and alpha components (r,g,b,a), multiply the
matrix [r g b a 1] by the color matrix above.
You should have noticed that there are 3 shear factors for each color component. The 3 factors
represent the component's relation with each of the three other components.
Similarly There are also 3 rotation factors for each component. If we eliminate the alpha
component, the total of all rotation factors becomes 6. Here are the matrices necessary to
perform all 6 types of rotations assuming that the rotation angle is (a):
----------------------------------------------------------------------------------------------
[ cos(a) sin(a) 0 0 0 ] [ 1 0 0 0 0 ] [ cos(a) 0 -sin(a) 0 0 ]
[-sin(a) cos(a) 0 0 0 ] [ 0 cos(a) sin(a) 0 0 ] [ 0 1 0 0 0 ]
[ 0 0 1 0 0 ] [ 0 -sin(a) cos(a) 0 0 ] [ sin(a) 0 cos(a) 0 0 ]
[ 0 0 0 1 0 ] [ 0 0 0 1 0 ] [ 0 0 0 1 0 ]
[ 0 0 0 0 1 ] [ 0 0 0 0 1 ] [ 0 0 0 0 1 ]
Rotation of red toward green Rotation of green toward blue Rotation of blue toward red
----------------------------------------------------------------------------------------------
[cos(a) -sin(a) 0 0 0 ] [ 1 0 0 0 0 ] [ cos(a) 0 sin(a) 0 0 ]
[sin(a) cos(a) 0 0 0 ] [ 0 cos(a) -sin(a) 0 0 ] [ 0 1 0 0 0 ]
[ 0 0 1 0 0 ] [ 0 sin(a) cos(a) 0 0 ] [-sin(a) 0 cos(a) 0 0 ]
[ 0 0 0 1 0 ] [ 0 0 0 1 0 ] [ 0 0 0 1 0 ]
[ 0 0 0 0 1 ] [ 0 0 0 0 1 ] [ 0 0 0 0 1 ]
Rotation of green toward red Rotation of blue toward green Rotation of red toward blue
----------------------------------------------------------------------------------------------
As mentioned before, always start with the identity matrix when you try to create the necessary
matrix for an operation. The identity matrix in this case is:
[ 1 0 0 0 0 ]
[ 0 1 0 0 0 ]
[ 0 0 1 0 0 ]
[ 0 0 0 1 0 ]
[ 0 0 0 0 1 ]
How to supply the matrix data to method gm("sc"):
-------------------------------------------------
(1) Decide upon the operation(s) which you like to apply to each color and form the necessary
matrix for each operation (on paper)
(2) Multiply all matrices together at the order which you see necessary to come up with the
final color transformation matrix.
(3) Assign the final matrix elements to the elements of array CMF[][]. Array CMF[][] is
a 5X5 array of float type which has been predefined for you. As an example, If the data
at the matrix element located at row number (0) and column number (2) was 5, make the
assignment: CMF[0][2]=5;
In order to make your job easy, the array comes to you with the identity matrix already
assigned to it. So you overwrite the elements which you like to change only. The rest
should be kept as is.
For example, if all you want to do was to scale the red component of the color by a
factor of 2, you'll only need to make the assignment:
CMF[0][0]=2;
and the color matrix array will be ready to go.
REMARK:
=======
A translation of (0.5) does not mean adding (0.5) to the color component. It actually means
adding (0.5 * 255 = 127.5) The GDI+ uses different system to represent color components within
the color matrix.
===========================================================================================
Example 3: Create a bitmap and draw a color pattern on it, then show how applying different
setups to the color matrix and modifying the image accordingly can change the look of the
image.
===========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
float sine,cosine; // Define 2 var's for sin & cos an angle
//---------------------- Create new (bip), Draw test pattern on it ---------------------
lf=125;of=60;gm("bn");gm("sdb"); // Create 125 X 60 (bip), set as graph. device
cls="r5";gm("sps"); // Create solid paint brush of light red
jf=-50;lf=25;of=50;gm("crf"); // Draw-fill a rectangle
cls="G5";gm("sps"); // Change color to dark green
jf=-25;lf=25;of=50;gm("crf"); // Draw-fill another rect beside first one
cls="b0";gm("sps"); // Change color to pure blue
lf=25;of=50;gm("crf"); // Draw-fill another rect
cls="s9";gm("sps"); // Change color to white
jf=25;lf=25;of=50;gm("crf"); // Draw-fill another rect
cls="S9";gm("sps"); // Change color to black
jf=50;lf=25;of=50;gm("crf"); // Draw-fill another rect
gm("sdd"); // Switch graphical out device back to (bio)
Bitmap bt=bip; // Save (bip) temporarely
jf=-300;kf=125;gm("br"); // Render (bip) as is
//---------- Show the effect of different ColorMatrix setups on resulting image ---------
bip=bt;gm("scn"); // Reset old attributes, prepare for new ones
oc='x';CMF[0][0]=0.5f;gm("sc"); // Set ColorMatrix attrib at rScale=0.5
gm("ba");jf=-100;kf=125;gm("br"); // Apply attributes to (bip) then draw it
bip=bt;gm("scn"); // Reset old attributes, prepare for new ones
oc='x';CMF[1][1]=CMF[2][2]=0.5f;gm("sc"); // gScale=bScale=0.5
gm("ba");jf=100;kf=125;gm("br"); // Apply attributes to (bip) then draw it
bip=bt;gm("scn"); // Reset old attributes, prepare for new ones
oc='x';CMF[4][0]=0.75f;gm("sc"); // rTrans=0.75
gm("ba");jf=300;kf=125;gm("br"); // Apply attributes to (bip) then draw it
bip=bt;gm("scn");
CMF[4][0]=CMF[4][1]=CMF[4][2]=0.5f; // Transform red, green & blue by 0.5 pixels
gm("sc");
oc='x';gm("ba");jf=-300;kf=0;gm("br");
bip=bt;gm("scn");
oc='x';CMF[1][1]=1.25f;CMF[4][1]=0.5f; // gScale=1.25, gTrans=0.5
gm("sc");
gm("ba");jf=-100;kf=0;gm("br");
bip=bt;gm("scn");
oc='x';CMF[0][1]=0.25f;gm("sc"); // grShear=0.25
gm("ba");jf=100;kf=0;gm("br");
bip=bt;gm("scn");
oc='x';CMF[2][0]=CMF[2][1]=0.5f;gm("sc"); // rbShear=gbShear=0.5
gm("ba");jf=300;kf=0;gm("br");
bip=bt;gm("scn");
oc='x';CMF[1][0]=0.25f;gm("sc"); // rgShear=0.25
gm("ba");jf=-300;kf=-125;gm("br");
bip=bt;gm("scn");
od=90;js="sin";um("mt");sine=(float)od; // Get sin(90), assign it to (sine)
od=90;js="cos";um("mt");cosine=(float)od; // Get cosine(90), assign it to (cosine)
CMF[0][0]=CMF[1][1]=cosine; // Make the setups for color rotation
CMF[0][1]=-sine;CMF[1][0]=sine; // of red component toward green one
oc='x';gm("sc");
gm("ba");jf=-100;kf=-125;gm("br");
bip=bt;gm("scn");
CMF[1][1]=CMF[2][2]=cosine; // Make the setups for color rotation
CMF[2][1]=-sine;CMF[1][2]=sine; // of green component toward blue one
oc='x';gm("sc");
gm("ba");jf=100;kf=-125;gm("br");
bip=bt;gm("scn");
CMF[0][0]=CMF[2][2]=cosine; // Make the setups for color rotation
CMF[0][2]=-sine;CMF[2][0]=sine; // of blue component toward red one
oc='x';gm("sc");
gm("ba");jf=300;kf=-125;gm("br");
//---------------------------------- Text Display -------------------------------------
fns="crb16"; // Set font
cls="r0";gm("sps"); // Prepare red pen
kf=175;os="USING THE COLOR MATRIX FOR COLOR EDITING";gm("ctf");
// Display title
cls="b0";gm("sps"); // Prepare blue pen
kf=85; os=" Original rScale=0.5 gScale=bScale=0.5 rTrans=0.75 ";
gm("ctf");
kf=-40; os="r,g,b Trans=0.5 gScale=1.25,gTrans=0.5 grShear=0.25 rbShear=gbShear=0.5";
gm("ctf");
kf=-165;os="rgShear=0.25 g-r Rotation g-b Rotation b-r Rotation ";
gm("ctf");
} // Display all other text
}
===========================================================================================
Can you make sense of the results you get from the Color Matrix?
----------------------------------------------------------------
The Color Matrix is a very powerful tool. It must be very useful to the experts in this
field. However, to a normal person, some of the logics involved are hard to make sense of.
Here is an example:
Let us assume that your image was nothing but a white rectangle and you decided to increase
its red component by 50%. So you made the assignment (CMF[0][0]=1.5f) Guess what the white
color will turn into. May be your answer is "no change" since the red component in the white
color was already at its maximum value of (255) before the transformation took place.
This is not what you get. The reason is that whenever the ".NET Graphics Device Interface"
(GDI+) finds that a color component was going to exceed (255), it subtracts (255) from the
result leaving only the remainder. In our case the remainder is (255*1.5 - 255 = 127.5) So
the red component actually went down in value causing the resulting color to look like
"Cyan".
What makes things even harder to make sense of, is that the formula mentioned above is used
by GDI+ whenever the overflow is caused by scaling or shearing. If it was caused by
translation the value of the red component could have stayed the same at 255 and the rectangle
could have remained white.
SUGGESTION:
===========
We are not sure why the GDI+ uses this logic. However we think that the logic which can make
an ordinary person able to make sense of the results obtained by using the color matrix
would require the following procedures:
(1) The 3 color components should be computed normally to start with, regardless to overflow
or underflow and regardless to the operation(s) performed.
(2) If any component was found to be over 255, its value should be reduced to 255 and the other
two color components should be reduced by the amount of the overflow.
(3) If any component was found to be negative its value should be increased to zero and the
other two color components should increase in value by the absolute value of the negative
amount.
If these rules have been applied, the resulting color components of the white rectangle which
has been used to illustrate this problem could have become:
Red: 255 Green: 127.5 Blue: 127.5
and the color of the white rectangle could have become light red.

============================================================================================
IMAGE FILES
===========
The Bitmap Object:
==================
The Bitmap is an array of bits which specifies the color of each pixel in a rectangular array
of pixels. The number of bits representing each color can be any number between 1 and 32. For
the bitmaps we use, the number is 32, eight for each of the color components red, green,blue
and alpha. This means that the decimal value of each component can be 0:255.
The color palette:
------------------
Some bitmaps, especially the ones which use small amounts of bits to represent colors contain
tables called color pallettes. The tables contain lists of colors and indices to them. The
indices are used in the color tables to represent the colors instead of the colors themselves.
The image file formats:
=======================
BMP format:
-----------
The common type uses 24 bits, however their number of bits which represent colors can be 1:64.
This value is specified in the file header. Bitmap files are not compressed which is good for
quality but not for size.
GIF (Graphics Interchange format):
----------------------------------
This type is good for animation since it can carry more than one frame and allows one color to
be designated as transparent. It is more suitable for drawings than for photographs. It is
limited to 8 bits/color. It is compressed but no information is lost in the process. It's not
a public property like other formats. One company claims copyright on this file format.
PNG (Portable Network Graphics):
--------------------------------
Similar to "GIF" except that it can represent colors with 8:48 bits and can store alpha values.
JPEG (Joint Photographic Experts Group):
----------------------------------------
This type is more suitable for photographs than for drawings. It can be made with different
quality factors. The higher the quality factor the lower the compression used.
EXIF (Exchangeable Image File):
-------------------------------
Similar to "jpeg" except that it contains useful information for digital camera users like
date picture was taken, shutter speed and camera make & model.
TIFF (Tag Image File Format):
-----------------------------
This is a flexible format in which many encoding parameters are settable. It allows storing
many kinds of information. It allows variety of compression algrithms and color depth settings
It can also store more than one image per file.
MetaFiles:
----------
This is a unique image file format. Instead of storing color information for each pixel of the
image, metafiles store instructions and settings for how to draw the image. We are going to
be discussing this fascinating image format in details soon.
Saving an image file:
=====================
If you check "PC# methods", you'll see that method gm() at modes starting with "bs" is used
for saving the present bitmap object (bip) into file.
If you like to use default settings, use the 3-character modes. For example, use mode "bsj" to
save (bip) into a file using "jpeg" format with default settings.
If you like to set your own encoding parameters, use mode "bs". At this mode, you can specify
the color depth, which means the number of bits used to represent a color, the compression
scheme and the color quality. Additionally, you can set the image file as a multi-frame file
into which you can store more than one bitmap object.
There are limitations. According to Microsoft documentation, the current GDI+ version only
allows few of the setups planned. Additionally, each file format allows only few of its
parameters to be settable. So, be prepared to get the error message "Parameter is not valid"
sometimes when you try different setups.
Multi-frame files:
------------------
We'll see in the next example how to set a "TIFF" file to be a multi-frame image file and
store three images into it. Frames are planned to be of three types:
(1) Time-based for annimation.
(2) Resolution-based which stors multi-resolution versions of one image.
(3) Page-based which stores any number of different images.
Unfortunately, the only type which is currently ready for use is the page-based.
Reading a multi-frame file:
---------------------------
There are two modes in method gm() which perform operations that require reading image flles.
Mode "cid" which draws an image on the graphical output device and mode "blf" which creates
a new bitmap and loads an image file into it. Both modes can handle multi-frame files and can
operate on a particular frame within a file if you supply its index assigned to (i).
Mode "blf" additionally checks the file you supply and counts the number of frames it contains.
It returns the count to you assigned to (os) The reason it assigns the count to (os) although
it's an integer is that method gm() resets (o) and most other o-based numerics at its end.
Next example will show how to read and display the images stored into a multi-frame file.
Parameters used by gm("bs"):
----------------------------
i : Color depth. Number of bits which represnt a color.
k : Quality factor. A number in the range 1:100 which specifies quality. The higher the
quality the less the level of compression used.
ks : Compression scheme. This can be: "none", "rle", "lzw", "ccitt3" or "ccitt4".
ib : Multi-frame flag. ib=true means set file for multi-frame use and store all the bitmap
images assigned to the bitmap array BIP[] into it. ib=false means set the file for
single frame use and store (bip) into it.
bip: Image to be stored into a single frame file.
BIP: Array of images to be stored into a multi-frame file.
===========================================================================================
Example 4:Create 3 bitmap objects using the 3 files icon.bmp, flower.jpg and pix.jpg and
save them all into a single multi-frame TIFF file. Set the color depth for all at 24 bits
and use LZW compression. After the file is saved, read it back, get and display its frame
count and display the 3 images it contains at different locations.
===========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
//------------------------------ Saving (encoding) file -------------------------------
fls="images\\icon.bmp";gm("blf");BIP[0]=bip; // Load 1st file and assign it to BIP[0]
fls="images\\flower.jpg";gm("blf");BIP[1]=bip; // Load 2nd file and assign it to BIP[1]
fls="images\\pix.jpg";gm("blf");BIP[2]=bip; // Load 3rd file and assign it to BIP[2]
// Parameters: Color depth=24 bits, LZW compression, multi-frame tiff file format
i=24;ks="lzw";ib=true;fls="test.tiff";gm("bs");// Save file using encoder parameters
//------------------------------ Reading (decoding) file ------------------------------
fls="test.tiff";i=0;gm("blf"); // Load frame 0 of file. Get frame count.
cls="b0";gm("sps");fns="trb20"; // prepare color and font for text display
os="File "+fls+" contains "+os+" frames."; // Prepare frame count message
kf=-125;gm("ctf"); // Display message
jf=-250;gm("br"); // Display image at frame 0.
fls="test.tiff";i=1;gm("blf");jf=0;gm("br"); // Get & display image at frame 1.
fls="test.tiff";i=2;gm("blf");jf=250;gm("br"); // Get & display image at frame 2.
}
}

============================================================================================
METAFILES
=========
Meta files are unique image files. Most image files contain color information for the pixels
which make an image while metafiles contain instructions and settings for how to draw the
image. Metafiles have the following advantages over other image file types:
(1) Smaller size. While the size of a regular image file depends on the size of the image
it represents, the size of a metafile depends on how sophisticated the drawing
instructions are. Normally, a metafile is considerably smaller in size than other image
file types with comparable quality.
(2) Maximum quality. When a metafile runs, it reproduces the image from scratch on the machine
which runs it. No drop in quality can occur due to data compression or any other reason.
(3) Flexiblity. They are made of small size records. You can run some and skip the rest.
USING METAFILES:
================
As you expect, we have developed the means to make you able to handle metafiles in an
extremely simple and pleasant manner. You can perform 3 operations on a metafile:
(1) Display the file as one piece. This can be done by simply assigning the file name to (fls)
and calling gm("cid") This mode is general to all image file types.
(2) Record a metafile. You start by calling gm("mow") to open the file for "writing". Once
this is done, you can make the metafile your "Graphical output device" by calling gm("sdm")
Anything you draw thereafter will be automatically recorded into the file. At the end, you
close the file by calling gm("mc")
(3) Read a metafile record by record. You start by calling gm("mor") to open the file for
"reading". Once this is done, method update() will be receiving all record data one by one.
Within method update(), you can alter the data if you like, but you don't have to do
anything other than calling gm("mr") to read and display the graphical data as is. At the
end you close the file by calling gm("mc")
Using blocks within a metafile:
-------------------------------
To allow you more control and power, we made it possible for you to divide your file into
blocks, just like you do with your code. When you read the file you can fast forward it to a
specific block before you start reading the data. PC# saves block numbers into the file in
the form of comment records
While writing to the file, at any moment you can call gm("mwb") to write the block number
number which you supply assigned to (bli) into the metafile.
While reading, you always have the option of either calling gm("mr"), to read and display the
record as explained above, or calling gm(mrb) with a block number assigned to (bli) to
perform a fast forward operation. The file will be searched through until the comment record
which contains the block number is reached.
Flexibility while writing into the metafile:
--------------------------------------------
While writing into the metafile, you don't have to keep it as your graphical output device all
the time. As you know drawing sometimes requires other devices to be made the graphical output
device temporarely like we do sometimes with (bip) Therefore, we have made it possible to
switch devices and return back to the metafile without losing file integrity.
You need to know here that graphical data is written into the metafile only when the file is
the graphical output device. During the time another device is the output device, the file
stays on halt.
==========================================================================================
Example 4: This example will demonstrate how to draw a red and a blue circles directly into
a metafile. At the middle of the operation, the graphical output device will be switched to
the default bitmap and a green circle will be drawn to it before it will be switched back to
the metafile.
At the end the file will be displayed in full using method gm("cid") on the default bitmap.
Since the green circle was drawn on the default bitmap, the three circles will show together.
===========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
//--------------------------------- Writing to file ----------------------------------
fls="test.emf";gm("mow"); // Open metafile for writing
// Draw red circle into file
gm("sdm"); // Set it as the graphical output device
bli=1;gm("mwb"); // Write "block=1" label
cls="r0";gm("sps"); // Create solid red pen
jf=-150;lf=of=150;gm("cef"); // Draw-fill red circle at (-150,0)
// Draw green circle on the screen
gm("sdd"); // Switch graphical output device to (bio)
cls="g0";gm("sps"); // Create solid green pen
lf=of=150;gm("cef"); // Draw-fill green circle at center
// Draw blue circle into file
gm("sdm"); // Return graphical output device back to metafile
bli=2;gm("mwb"); // Write "block=2" label
cls="b0";i=5;gm("sps"); // Create solid blue pen
jf=150;lf=of=150;gm("cef"); // Draw-fill blue circle at (150,0)
gm("sdd"); // Switch graphical output device to (bio)
gm("mc"); // Close metafile
//----------------------------------- Display file ------------------------------------
fls="test.emf";gm("cid");
}
}
===========================================================================================

============================================================================================
Reading the metafile data back:
-------------------------------
Immediately after opening the file for reading, method update will be receiving record data
in order starting with first record in the file. As you know, each call to method update must
be accompanied with an assignment to (cs) which identifies the source of the event. The
metafile call is identified with the assignment (cs="mf") Additionally method update()
receives the following with each call:
(1) Metafile block number (mbi): As you have seen in the previous example, each block of code
in the metafile can be identified with a block number. The block number label is placed as
a comment record at the top of the code block.
Before method update() is called with a new record data, PC# software checks to see if it
was a new block label. If it was found to be, it reads the block number and assigns it to
(mbi) Additionally it sets (ob) to let you know that this is a block label record.
If the next record was a data record, the flag (ob) is reset and the assignment to (mbi)
stays the same since the data record belongs to the same block number. The new assignments
are repeated with all following records until a new block label record is encountered.
(2) Block label record identifier (ob): As described in (1), (ob=true) means that the current
record is a block identifier record.
(3) End of file record identifier (jb): It is essential to check the assignment to (jb) after
each call is received by method update() Whenever (jb=true), gm("mc") must be called in
order to close the operation.
(4) Type of present record (mrp)
(5) Metafile Flags (of)
(6) Data Size (od)
(7) Data (OY)
You'll need to use items (4:7) if you like to do more than just displaying file's data without
modification. We are not going to use them in the next example.
How to design your metafile reading program?
--------------------------------------------
As you know, method run() starts immediately after methods init() and setup() are executed.
Therefore, opening the file should be in run().
You should prepare a block in method update() which handles the metafile data. The same block
will be called again and again with the data for each record, so you must make sure to
condition your statements so that each statement is executed only when the right record comes.
After receiving each record you should do one of the following:
(1) Call method gm("mr") to read and display the data. Make sure to keep the received data
(mrp,of,od and OY) unchanged since they are used by this method.
(2) Call method gm("mrb") to fast forward through the file until a new block is reached. You
supply the new block number assigned to (bli)
(3) Return without calling either method.
When you receive the label record of a new block, you have a chance to do some operations
which are irrelated to the metafile data, like interfacing with the user or displaying
additional messages or drawings which are necessary for the job. The record itself contains
no data which can be displayed.
===========================================================================================
Example 7: In this example, we are going to read back file "test.emf" and display its
contents. Immediately before each of the two circles is displayed, the display process will
be interrupted with a message to inform the user of the coming display.
===========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void update() {
if (cs.Equals("mf")) { // If this was a metafile record read call:
if (mbi<1) { // If file pointer was before block 1:
bli=1;gm("mrb");return; // Fast Forward to block 1.
}
if (mbi==1) { // If pointer is now at or after block 1's label:
if (ob) { // If "at the label" display message then
os="Click to get the red circle";cm("d");
return; // return since label record has nothing to display
}
gm("mr"); // If at data records following block 1's label,
} // display it.
if (mbi==2) { // If pointer is now at or after block 2's label:
if (ob) { // proceed similarly.
os="Click to get the blue circle";cm("d");
return;
}
gm("mr");
}
if (jb) gm("mc"); // Close metafile.
}
}
public override void run() {
jf=0;kf=0;fls="test.emf";gm("mor"); // Open metafile for read , set display center point
} // at form's center.
}

============================================================================================
IS THERE ANYTHING WHICH WE CANNOT DO TO AN IMAGE?
=================================================
Let us list what we are able to do:
(1) We can draw the image at any location using gm("br")
(2) We can set the image as the graphical output device using gm("sdb") and draw anything on
its surface, then change graphical output device and draw the modified image on the new
device.
(3) We can set Affine transform to rotate the image by any angle using gm("stu") then draw the
image while applying the transform using gm("brt")
(4) We can apply any shearing to the image using the same methods as in (3)
(5) We can scale the image by any amount using several means, one of them is the same as in (3)
(6) We can crop the image and we are not limited to rectangular cropping. The cropping can
take any shape like a circle, ellipse or hexagon. We are going to see example on that next.
(7) We can crop and reduce the file size of the image. This has been done in example 11 of
the "Drawing" chapter and will be demonstrated in a new example.
(8) We can save the image into file of any format. This has been demonstrated in the same
example.
(9) We can edit the color of all pixels of the image using several methods. Color mapping
and color matrix are some of them.
10) We can save more than one image in a single file and can select the color depth and the
compression scheme to be used.
11) We can create an image metafile which stores the image in the form of drawing instructions.
12) We can use the image to tile any shape object with tiling pattern which is settable by
image attributes.
We believe that everything possible to do to an image has been covered. However, cropping,
shearing and rotaing an image are good to be demonstrated in the next example.
===========================================================================================
Example 7: Show how to give an image a hexagonal borders. Also show how to apply shearing
and rotation to an image.
===========================================================================================
public class a : pcs {
public override void init() {
base.init();
}
public override void run() {
i=0;fls="images\\flower.jpg";gm("blf");
// Create new (bip) and load an image file into it.
lf=6;of=200;gm("c="); // Create a Hexagon
gm("gc"); // Set the hexagon as the clip area.
gm("br"); // Draw the image.
gm("gcn"); // Cancel previous setup of Clip area
ad=10;lf=-250;gm("stu"); // Create Affine xform to rotate image 10 degrees and
gm("brt"); // move it to left. Draw image applying the transform
id=0.3;lf=250;gm("stu");gm("brt"); // Draw image after applying horiz shear of 0.3
}
}

===============================================================================================
Cropping with file size reduction:
----------------------------------
If you like to eliminate the empty area around an image completely in order to reduce its file
size, proceed as follows:
(1) Create a new (bip) and Load the image file into it at full scale.
(2) Resize Form to the wanted reduced size of the image. (bio) will automatically be recreated
in order to make it match the new size of the Form.
(3) Draw (bip) on (bio) at coordinates which allow the wanted portion of the image to appear.
(4) Make (bip) another reference to the default Bitmap object (bio)
(5) Save (bip)
===============================================================================================
Example 8: Eliminate empty margins around the image in "images\\flower.jpg" and save the reduced
image into file "ReducedImage.jpg".
===============================================================================================
public class a:pcs {
public override void run() {
fls="images\\flower.jpg"; // Create a new (bip) and
lf=of=0;gm("blf"); // load file into it at scale 1:1
j=140;k=170;cm("fs"); // Resize form (and bio) to wanted image size.
kf=10;gm("br"); // Draw image at coord's which allow wanted section
bip=bio; // to appear. Assign (bio) object to (bip)
fls="ReducedImage.jpg";gm("bsj"); // Save (bip)
}
}
-----------------------------------------------------------------------------------------------
Compile and run this program and compare the new file size to the original one. You should find
that the file size which has been originally 11,142 bytes has been reduced to 9,411 bytes.
===============================================================================================

================================================================================================
PAINTING SHAPE OBJECTS WITH GOLD AND CHROME COLORS (For versions 4.37 and higher):
==================================================================================
Notice that the technique which we used in example 7 to crop an image can be used for special
painting applications.
For example, if you like to create any shape and paint it with "gold" or "chrome" color, you may
use the "gold.bmp" and "chrome.bmp" images which are available in the "image" subfolder of your
working directory. Here is an Example:
================================================================================================
Example 9: Create a Pentagon and paint it with gold.
================================================================================================
public class a : pcs {
public override void run() {
j=800;k=400;cm("fs"); // Resize form to 800 X 400
// Create a new (bip) of a size which exceeds the wanted shape's size and load it with
// the image file "images\\gold.bmp".
i=0;lf=of=300;fls="images\\gold.bmp";gm("blf");
lf=5;of=200;gm("c="); // Create a Pentagon
gm("gc"); // Set the Pentagon as the clip area.
gm("br"); // Draw the image.
gm("gcn"); // Cancel previous setup of Clip area
}
}
================================================================================================

================================================================================================
And here is another example:
================================================================================================
Example 10: Draw the text "PC#" in gold over a surface of chrome.
================================================================================================
public class a : pcs {
public override void run() {
j=800;k=400;cm("fs"); // Resize form to (800 X 400) pixels
// Create a new (bip) of Form size. Load it with "chrome.bmp" image and draw it.
lf=800;of=400;fls="images\\chrome.bmp";gm("blf");gm("br");
// Creating shape object of each char of the string "PC#" and painting it with image.
xs="PC#";fns="trb250"; // Assign the string to (xs) Set font
int pos=-200; // Startup postion
lf=of=300;fls="images\\gold.bmp";gm("blf");
// Load (bip) with gold image
for(c=0;c< xs.Length;c++) { // Scan (xs) characters
os=xs.Substring(c,1); // Assign each char to (os)
jf=pos;gm("ct");gm("gc"); // Make (gpp) of the char a clip area
jf=pos;gm("br");gm("gcn"); // Draw (bip) abve char then reset clip area
pos+=200; // Move drawing position to next char's place
}
}
}
================================================================================================
You can use this technique to paint any shape object which you create with variety of images. You
may create a subfolder and store a variety of surface images to paint your drawings with. The
subfolder should contain an inventory of images of expensive wood surfaces, wallpaper patterns,
sky clouds, water, ... etc.
================================================================================================

================================================================================================
Obtaining the GraphicsPath object (gpp) of an image (for PC# 4.40 and later versions):
======================================================================================
The ability of the GraphicsPath object (gpp) to be made a clip area has proven to be very useful.
Images cannot be made clip areas. However starting at "PC# version 4.40", you can call method
gm("bg") to obtain the (gpp) of the outlines of an image.
The method likes to see a white background around image at all directions and a clear difference
in color between that background and the outer border line of the image.
The two sketches "vase.bmp" and "base.bmp" which are available in the "images" subfolder of your
working directory qualify perfectly for this job.
If you like to use other images, the best way is to print the image, use a pair of scissors to
cut along its outer lines then stick it on a white piece of paper which exceeds it in size. Now
you should scan the image and save it into an image file.
If method gm("bg") could not make the (gpp) successfully, you need to mark the image outlines
with a black pen and try again.
The (gpp) returned comes centered at the origin, so you need to move it to where it should be
before you use it.
Image scanning density:
-----------------------
When method gm("bg") computes the GraphicsPath which represents the outlines of the image, it
centers the image at the origin and performs a radial scan each one degree angle to determine
each point of the GraphicsPath. This means that it figures out the locations of 360 points on
the image outlines and connects them together to make the GraphicsPath.
If you have studied the chapter of "Drawing II", this must have reminded you with 3D drawing at
the default density (dna=1) when each unit cylinder of the 3D figure is divided into 360 sectors.
The two processes are very similar.
Therefore, we have decided to make method gm("bg") use the same logic concerning the density it
does its job with. At the default density (dna=1), the GraphicsPath is made with 360 points. If
you make the assignment (dna=2) the path will be made with (2*360=720) points and if you make the
assignment (dna=0.5), it will be made with 180 points.
================================================================================================
Example 11: Obtain the GraphicsPath objects of the two sketch images "vase.bmp" and "base.bmp"
then use the same technique used in examples 9 and 10 to paint them with different images.
================================================================================================
public class a : pcs {
public override void init() {
j=825;k=425;dm("s"); // Resize Form
base.init();
}
public override void run() {
//----------------------------------- Left side drawing ----------------------------------
// Obtaining (gpp) for the sketch image "vase.bmp" and painting it with "flower.jpg" image
fls="images\\vase.bmp";gm("bg"); // Obtain (gpp) for the outlines of image "vase.bmp"
jd=kd=0.22;lf=-250;gm("stu");gm("gt");// Scale (gpp) by 0.22 and move it to left without drawing
gm("gc"); // Set (gpp) as the clip area.
i=0;lf=of=180;fls="images\\flower.jpg";gm("blf");
// Start a new bip using "flower.jpg" image scaled to be
jf=-250;gm("br"); // slightly larger than (gpp) and draw it over (gpp)
gm("gcn"); // Cancel previous setup of Clip area
//----------------------------------- Right side drawing ----------------------------------
// Obtaining (gpp) for the sketch image "vase.bmp" and painting it with "gold.bmp" image
fls="images\\vase.bmp";gm("bg");
jd=kd=0.22;lf=250;gm("stu");gm("gt");
gm("gc");
i=0;lf=of=300;fls="images\\gold.bmp";gm("blf");
jf=250;gm("br");
gm("gcn");
//----------------------------------- Drawing at center ----------------------------------
// Obtaining (gpp) for the sketch image "base.bmp" and painting it with "chrome.bmp" image
fls="images\\base.bmp";gm("bg");
jd=kd=0.2;lf=0;gm("stu");gm("gt");
gm("gc");
i=0;lf=of=300;fls="images\\chrome.bmp";gm("blf");
jf=0;gm("br");
gm("gcn");
}
}
================================================================================================
Giving you more control over method gm("bg") operation:
-------------------------------------------------------
Starting at PC# version 4.43 you can supply the method with the coordinates of a point on the
image's background to use as a background color sample. Since you cannot expect the colors of all
points on the background to be identical to the sample point's color, you assign to (id) the
allowed deviation percentage amount which the color components at all background points must not
go beyond.
The value you assign to (id) is a percent amount of the maximum value for each component of the
color which is 255. So (id=25) means that acceptable deviation is (25% of 255 which is 64)
Example: If the method found that the red, green and blue color components at the sample point were
(r,g,b) and you supplied (id=25), any point with red component beyond the range (r+64 to r-64) will
will not be considered part of the background.
If you assign no value to (id), the default white background described above will be considered what
you have and (jf,kf) will be neglected.
Here is an example which can eliminate the background of the image "flower.jpg".
----------------------------------------------------------------------------------------------
public class a : pcs {
public override void init() {
j=825;k=425;dm("s"); // Resize Form
base.init();
}
public override void run() {
//---------------------------------- Sampling the background ------------------------------
// By looking at the image we may assume that a point with (x,y) coord's which are 10 pixels
// less than the (x,y) coord's of image's right and top edges is guaranteed to be on the
// background. So its color can be used as a background color sample.
fls="images\\flower.jpg";gm("blf");gm("br"); // Draw the image at center
int xf=bip.Width/2-10;int yf=bip.Height/2-10; // Make (xf,yf) the sample point coord's.
cls="g0";gm("sps"); // Create green pen to highlight sample point loc.
jf=xf;kf=yf;lf=of=5;gm("ced"); // Draw a small green circle around sample point
os="Is Sample point OK?";ks="yn";cm("d"); // Ask user if sample point was at correct locatn
if (os=="n") {cm("fe");return;} // If not, exit and try another location.
cls="s7";gm("ec"); // Else, erase screen.
//---------------------------- Creating the GraphicsPath object -----------------------------
// This time, we are going to supply coordinates of a background sample point and tell the method
// that we accept a maximum deviation of + or - 25% from the sample pt's color.
fls="images\\flower.jpg";jf=xf;kf=yf;id=25; // Call gm("bg") with (jf,kf)=Sample pt coord's
gm("bg"); // and id=Allowed Deviation% for all color comp's
//------------------------- Displaying image after removing background -----------------------
gm("gc"); // Make (ggp) a clip area
gm("blf");gm("br"); // and draw image above it.
}
}
================================================================================================

================================================================================================
Editing image's colors (for versions 4.42 and later)
====================================================
We have discussed before several means for editing the internal colors of an image. Let us look
at them again:
(1) Editing the image one pixel at a time: Example 1 of this chapter showed how to do it. This
method of color editing is slow. However, in the chapter of "Boosting PC# speed further by
using C and Assembly" we have seen how to speed it up by using Assembly language.
(2) Color mapping: This method allows us to replace one single color with another. This is not
practically useful for most applications. This is because you can't know the red, green and
blue components of a color exactly by just looking at it.
(3) Color clearing: This method replaces a range of colors in the image with a transparent color.
When you add this ability to the ability of method gm("bg") which creates the GraphicsPath
object of the image's outlines and the technique we used in past examples to paint the path
with special colors like Gold and Chrome, you end with a new superior way to edit images colors.
About the next Example:
-----------------------
Here are the steps for replacing a range of colors which are found in an image with one new
color or with the colors of a surface image (like "gold.bmp" or "chrome.bmp")
(1) Obtain the GraphicsPath of its outlines and make it a clip area.
(2) Draw a painted rectangle with the wanted new color or the surface image atop the clip area.
(3) Assign the image to (bip) and use method gm("sc") to clear the unwanted color range.
(4) Draw the image above the clip area atop the new paint rectangle or surface image of step (2)
We are going to be replacing the black spots of the image "pattern1.bmp" with gold, chrome or red.
Notice that when a color looks black, this does not mean that its red, green and blue components
are necessarily zero's. Therefore, we'll be specifying a range of (0-120) for each of the three
components to identify the black color spots.
================================================================================================
Example 12: Show how you can modify the internal colors of the image "pattern1.bmp" (which is
available in the "images" subfolder of your working directory) by repainting the items which are
currently painted black with variety of colors and images.
================================================================================================
public class a : pcs {
public override void init() {
j=825;k=425;dm("s"); // Resize Form
base.init();
}
public override void run() {
//---------------------------------------- Display -----------------------------------------
// We are displaying the image 4 times. All are centered on the X-Axis at X-Coordinates which
// are assigned to the var's (a,b,c,d)
fns="trbu24";cls="o0";gm("sps");jf=0;kf=150; // Display title
os="REPAINTING IMAGES";gm("ctf"); // at center
a=-300;b=-100;c=100;d=300; // X-Coord's of the 4 image displays
//----------------- 1st image display: Original image with no modification ----------------
string fl1s="images\\pattern1.bmp"; // Assign the image file name to (fl1s)
fls=fl1s;gm("blf"); // Create a new (bip) and load the file into it.
fls=fl1s;jf=a;gm("br"); // Draw the image at the x-postion of (a)
//-------------- 2nd image display: Image after replacing black areas with gold -----------
fls=fl1s;gm("bg"); // Obtain (ggp) of image's outlines.
lf=b;gm("stu");gm("gt"); // Position (gpp) at point (b)
gm("gc"); // then make it the clip area
lf=of=225;fls="images\\gold.bmp";gm("blf"); // Assign gold surface image to (bip) Scale it
jf=b;gm("br"); // to exceed (gpp) in size then draw it above it
fls=fl1s;gm("blf"); // Create a new (bip) and load the file into it.
CLI=new int[]{0,0,0,0,120,120,120,0}; // Clear all colors whose (r,g,b) components are
oc='c';gm("sc");gm("ba"); // in range (0-120) Apply attributes to image.
jf=b;kf=0;gm("br"); // Draw the image at same position.
gm("gcn");gm("scn"); // Reset current clip area and image attributes.
//------------- 3rd image display: Image after replacing black areas with chrome -----------
fls=fl1s;gm("bg");
lf=c;gm("stu");gm("gt");
gm("gc");
lf=of=225;fls="images\\chrome.bmp";gm("blf");
jf=c;gm("br");
fls=fl1s;gm("blf");
CLI=new int[]{0,0,0,0,120,120,120,0};
oc='c';gm("sc");gm("ba");
jf=c;kf=0;gm("br");
gm("gcn");gm("scn");
//------------- 4th image display: Image after replacing black areas with red --------------
fls=fl1s;gm("bg");
lf=d;gm("stu");gm("gt");
gm("gc");
cls="r0";gm("sps");jf=d;lf=of=225;gm("crf"); // Draw-fill a red rect of size larger than image's
fls=fl1s;gm("blf");
CLI=new int[]{0,0,0,0,120,120,120,0};
oc='c';gm("sc");gm("ba");
jf=d;kf=0;gm("br");
gm("gcn");gm("scn");
//----------------------------------- Bottom Display ---------------------------------------
fns="crb20";cls="S9";gm("sps");jf=-16;kf=-150;
os="ORIGINAL GOLD CHROME RED ";gm("ctf");
}
}
=================================================================================================
REMARK:
=======
We can repeat the process to replace more of the image colors with different colors or with surface
images. Let us assume that in addition to replacing the black spots with blue, we like to replace the
white spots with gold. Here is how it could be done:
public class a : pcs {
public override void init() {
j=k=225;dm("s"); // Resize form at image's size only
base.init();
}
public override void run() {
//----------------------- First Step: Replacing white areas with gold ---------------------
cls="s9";gm("ec"); // Paint form white.
string fl1s="images\\pattern1.bmp"; // Assign the image file name to (fl1s)
fls=fl1s;gm("bg"); // Obtain (gpp) of image's outlines.
GraphicsPath gp1p=gpp; // and memorize it since will be used again,
gm("gc"); // then make it the clip area
lf=of=225;fls="images\\gold.bmp";gm("blf"); // Assign chrome surface image to (bip) Scale it
gm("br"); // to exceed (gpp) in size then draw it above it
fls=fl1s;gm("blf"); // Create a new (bip) and load the file into it.
CLI=new int[]{230,230,230,0,255,255,255,0}; // Clear all colors whose (r,g,b) components are
oc='c';gm("sc");gm("ba"); // in range (230-255) Apply attributes to image.
gm("br"); // Draw bip at center.
gm("gcn");gm("scn"); // Reset current clip area and image attributes.
bip=bio;fls="temp.bmp";gm("bsb"); // Make bip=bio and save it into a temporary file
//------------------------ Second Step: Replacing black areas with blue ---------------------
gpp=gp1p;gm("gc"); // Make same (gpp) a clip area again.
cls="b0";gm("sps");lf=of=225;gm("crf"); // Draw-fill a blue rect larger than image in size
fls="temp.bmp";gm("blf"); // Create a new (bip), load the temp file into it.
CLI=new int[]{0,0,0,0,120,120,120,0}; // Clear all colors whose (r,g,b) components are
oc='c';gm("sc");gm("ba"); // in range (0-120) Apply attributes to image
gm("br"); // Draw (bip) at center
gm("gcn");gm("scn"); // Reset current clip area and image attributes.
}
}
=================================================================================================