Personal C Sharp                                                         By  famsoft.org
HomeHow To StartExamples-DesktopExamples-WebPC# MethodsReference-DesktopReference-Web


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. } } =================================================================================================