Jump to content United States-English
HP.com Home Products and Services Support and Drivers Solutions How to Buy
» Contact HP
More options
HP.com home
HP PEX Implementation and Programming Supplement: HP9000 Series 700 Color Workstations > Chapter 10 Texture Mapping Tutorial

Using PEXlib for Texture Mapping

» 

Technical documentation

Complete book in PDF
» Feedback
Content starts here

 » Table of Contents

 » Glossary

 » Index

There are six main steps required to use PEXlib texture mapping; these steps are described in the next sections of the tutorial:

  • “Step 1: Setup ”

    1. Ensure texture mapping support.

    2. Inquire implementation dependent constants.

  • “Step 2: Texture Preparation ”

    1. Create a filtered map resource from the source texture map and import it into PEXlib.

    2. Create a texture map description resource.

  • “Step 3: Geometry Preparation ”

    1. Compute texture coordinates with PEXlib utilities.

  • “Step 4: Set up Texture Mapping Lookup Tables (LUTs) ”

    1. Create one or more Coordinate Source LUT entries to specify how a texture is mapped onto a primitive.

    2. Create one or more Composition LUT entries to describe how texture map data is combined with the existing color and alpha data of a primitive.

    3. Create one or more Sampling LUT entries to specify how a filtered texture map is sampled or accessed.

    4. Create one or more Binding LUT entries to associate a texture description with entries in the Coordinate Source, Composition, and Sampling LUTs.

  • “Step 5: Render ”

    1. Set up rendering options.

    2. Enable texture mapping for subsequent primitives.

    3. Activate texture(s).

    4. Render primitives.

  • “Step 6: Clean Up ”

    1. Free resources used by texture mapping.

The graphics pipeline — that sequence of steps your graphical data goes through in the process of getting from your model to the finished image on the display — is as follows:

  1. Prespecular texturing;

  2. Lighting and shading;

  3. Postspecular texturing;

  4. Depth cueing;

  5. Screen door transparency applied;

  6. Replacement rules are applied;

  7. Final alpha blending with the frame buffer (if supported in hardware).

Of course, your program may or may not use all of these features; the above merely shows the order in which the processes may occur.

Step 1: Setup

  1. Ensure texture mapping support. Setup for texture mapping ensures that texture mapping is supported by the current PEXlib implementation by calling PEXGetEnumTypeInfo. (Don't forget to call PEXFreeEnumInfo, when finished with the memory allocated by PEXGetEnumTypeInfo.) See also “Step 1: Setup ” in the Texture Mapping Overview.

  2. Inquire implementation dependent constants. In general, all applications should inquire texture mapping implementation dependent constants via PEXGetImpDepConstants. Different implementations of PEXlib may return different values for these constants that must be considered. For example, the constant PEXExtIDPowerOfTwoTMSizesRequired must be passed by the application to either PEXExtCreateFilteredTM or PEXExtCreateFilteredTMFromWindow.

Step 2: Texture Preparation

  1. Create a filtered map resource from the source texture map and import it into PEXlib.

    Texture preparation involves creating a filtered map (a "MIP map") from your source map using PEXExtCreateFilteredTM or PEXExtCreateFilteredTMFromWindow and importing that map into PEXlib via PEXExtCreateTM.

    Note that after PEXExtCreateTM has been called, PEXExtFreeFilteredTM can be called to reclaim memory used for the filtered map.

    (See also “User Interface Considerations for Creating Filtered Texture Maps ” and “Discussion: MIP Map ”.)

  2. Create a texture map description resource.

    Once the map has been imported into PEXlib, PEXExtCreateTMDescription must be called to combine texture identifier(s), parameterization and rendering information to form a texture map description. In addition to providing PEXExtCreateTMDescription with the texture resource identifier(s) returned by PEXExtCreateTM, three key parameters must be specified:

    • parameterization

    • param_data

    • rendering_order

    The parameterization parameter specifies how texture coordinates will be derived. Texture coordinates determine how a texture is mapped onto a primitive. The possible values for parameterization are PEXExtTMParamExplicit, PEXExtTMParamReflectSphereVRC, PEXExtTMParamReflectSphereWC, and PEXExtTMParamLinearVRC. The following list describes the different effects produced by using these different values.

    • PEXExtParamExplicit (view-independent, standard mapping)

      The application calculates texture coordinates or uses the client-side utilities PEXExtTMCoordFillAreaSetWithData, PEXExtTMCoordSetOfFillAreaSets, PEXExtTMCoordTriangleStrip, and/or PEXExtTMCoordQuadrilateralMesh.

      Visual result: The texture map is fixed to the primitive, regardless of the position of the camera or animation of the primitive.

    • PEXExtTMParamReflectSphereVRC (view-dependent, reflection mapping)

      Texture coordinates are determined by the PEXlib server with respect to the current camera position.

      Visual result: The texture map is reflected onto the primitive. If the camera moves, the texture map will appear to move along with it and the object will reflect essentially the same portion of the texture map regardless of the point of view.

    • PEXExtTMParamReflectSphereWC (view-dependent, reflection mapping)

      Texture coordinates are determined by the PEXlib server with respect to the current camera position.

      The texture map is reflected onto the primitive and produces "true" environment mapping. If the camera moves, the object will reflect a different portion of the texture map in much the same way as if you were to move around a chrome ball that reflects your environment.

    • PEXExtTMParamLinearVRC (view-dependent, standard mapping)

      Texture coordinates are determined by the PEXlib server with respect to the current camera position.

      Texture coordinates are determined by the PEXlib server with respect to a projection reference plane defined in view reference coordinates (VRC).

      At the time of activation (PEXExtSetActiveTextures), equations p0 and p1 are inversely transformed from VRC space back into Model Coordinate (MC) space. Once there, they define a projection function such that objects appear to "swim" through a solid field of texture coordinates. This technique can sometimes be very effective in revealing the surface contours of an object in motion if the right type of texture map grid is employed.

      It should be noted that the visual result of this technique is subject to the currently active view orientation, local and global transforms at the time of activation. If scaling and rotation are incorporated in any of these matrices, repetition and distortion of the texture field may result.

The distinction between view-dependent and view-independent texture coordinates is an important one. View-independent texture mapping results in a texture fixed on a primitive that does not change when the point of view changes. View-dependent mapping means that the apparent texture does change depending on the point of view of the camera.

When view-independent mapping (PEXExtParamExplicit) is desired, the texture coordinates can be calculated once for each primitive and need not be recomputed if the position of the camera changes. For this reason, the client-side utilities, PEXExtTMCoordFillAreaSetWithData, PEXExtTMCoordSetOfFillAreaSets, PEXExtTMCoordTriangleStrip, and PEXExtTMCoordQuadrilateralMesh are provided to pre-calculate the texture coordinates. These utilities are described in the next section.

View-dependent mapping, on the other hand, demands that the texture coordinates be calculated each time the view point is moved. In this case, it is logical for the server to calculate the coordinates each time a texture mapped primitive is rendered. View-dependent, server-side texture coordinates are derived for parameterization values PEXExtTMParamSphereVRC, PEXExtTMParamSphereWC, and PEXExtTMParamLinearVRC. The former two values cause a projection onto an infinite sphere to be used in calculating the texture coordinates, while PEXExtTMParamLinearVRC causes a linear projection to be used to compute the coordinates. See “Parameterization ”, below, for a more thorough explanation of the process of calculating texture coordinates.

Note that two of the four possible parameterization values, PEXExtTMParamReflectSphereVRC and PEXExtTMParamReflectSphereWC, result in "reflection mapping." The other two methods produce "standard" texture mapping. A reflection mapping differs from a "standard" texture mapping in that the texture coordinates for reflection mapping are based on the calculation of reflection vectors for each vertex in a primitive. The result is an object that reflects, or mirrors, the texture map. Reflection mapping can be likened to viewing the reflections of a room by looking at a shiny Christmas tree ornament. "Standard" texture mapping does not rely on reflection vectors at all. A texture is placed on an object much the same way as a piece of wrapping paper is applied to a gift.

Environment mapping is a form of reflection mapping where the texture map is a picture of the environment from the viewpoint of the object at the center of the environment. PEXlib reflection mapping can also be used to simulate chrome or other shiny materials. Because some shiny materials like chrome are not perfect reflectors, reflection mapping provides a relatively inexpensive way to simulate a shiny object.

The param_data parameter contains the linear equations p0 and p1 and a reflection_matrix. The values specified in p0 and p1 define the linear equations used for parameterization equal to PEXExtTMParamLinearVRC. The reflection_matrix can be used to orient a spherical or cylindrical projection object so that texture seams or distortions will appear where they are less noticeable once the texture is mapped onto a primitive. It is often desirable to align the axis of revolution of the projection object with the natural axis of symmetry for the geometrical object (if it has one). It is recommended that the matrix contain only 3D rotations and it should be noted that the matrix is never applied to the linear projection.

In addition to parameterization information, the rendering_order must be passed to PEXExtCreateTMDescription. The two alternative values are PreSpecular, meaning that any specular highlight is added after the texture component, and PostSpecular, which specifies that texture mapping affects the color after specular has been applied.

Parameterization

Surface parameterization is the name given to the process of generating texture coordinates. Texture coordinates (t0,t1) "tie" a 2D texture map to a 3D geometric model — a sphere, a cylinder or a plane:

Figure 10-3 Coordinate Systems of the Three Types of Projection Objects

Coordinate Systems of the Three Types of Projection Objects

Finding the correspondence between a 2D map and 3D object is not as trivial as it might appear. In the case of PEXlib, mathematical projections are used to derive the correspondence, in a two-step process.

First, the geometric model is projected onto a standard volume, or projection object, and second, the projection object is "unfolded" to a flat, 2D surface that corresponds to a 2D texture map.

Figure 10-4 "Unfolding" a Projection Object

"Unfolding" a Projection Object

These two steps are described in more detail below.

  1. The geometric model is conceptually placed in the center of a projection object. (PEXlib supports projection objects sphere, cylinder, and plane). For each vertex in the model, a vector is calculated that intersects the projection object.

    Figure 10-5 Geometric Model with Various Projection Objects

    Geometric Model with Various Projection Objects

    Projection objects of may be rotated using the matrix passed to PEXExtCreateTMDescription or the PEXExtTMCoord* utilities.

    Several different methods of projection, or methods to find the intersection, are supported including using a reflection vector, vertex coordinate, or vertex normal.

    For spherical and cylindrical projections, t0 corresponds to the intersection with the projection object in terms of an azimuth and will have a value between 0 and 2π. The t1 coordinate for a spherical projection corresponds to an elevation between -π/2 and π/2. The t1 coordinate for a cylindrical projection corresponds to a height on the cylinder. For a planar projection, t0 and t1 correspond to the abscissa and ordinate on the plane, respectively.

  2. The second step in the process involves unfolding the projection object into a flat plane which trivially maps to a 2D texture map. For a spherical projection, this means mapping from the range [0,2π] to [0,1] in X, and from the range [-π/2,π/2] to [0,1] in Y. For a cylindrical projection, [0,2π] maps to [0,1] in X and the [0,h] maps to [0,1] in Y, where h is the height of the cylinder. Finally, for planar projections, [0,w ] maps to [0,1] and [0,h] maps to [0,1] where w and h are the width and height of the projection plane, respectively.

    Before the texture coordinates are used to access the texture map, they are first transformed by the orientation matrix in the Coordinate Source LUT. This transformation effectively allows the texture to be translated, rotated and scaled before it is applied to the geometry.

User Interface Considerations for Parameterization

An application may allow an advanced user to choose between the many different parameterization methods supported by PEXlib. The user interface for the methods may be presented in terms of view independence, reflection mapping vs. "standard" texture mapping, and projection objects. To further control the generation of texture coordinates, a user may also need to be able to manipulate the projection object matrix for each projection. For explicit projections created by PEXExtTMCoord*, a user may be given a choice of using the vertex coordinates or vertex normals when creating the projection vector for a projection. This allows the user to further "tweak" the results of the texture coordinates calculations to produce the desired effects.

Note that although PEXlib supports powerful texture coordinate generation techniques, some advanced texture mapping applications may want to extend the capabilities further and allow users to modify individual texture coordinates interactively, thus achieving complete control over texture placement. Such an application would want to display the actual texture coordinates and the texture mapped object and allow the user to pick one or more coordinates and move them using a mouse or other input device. Another scheme would display the 2D texture map and overlay the 2D texture coordinates. This would allow the user to manipulate the coordinates in 2D, a much simpler operation than manipulation in 3D.

Step 3: Geometry Preparation

  1. Compute texture coordinates with PEXlib utilities.

    Recall from the “Step 2: Texture Preparation ” discussion, that when parameterization is set to PEXExtParamExplicit and passed to PEXExtCreateTMDescription, the texture coordinates are expected to be computed by the client-side PEXlib utilities (or by the application), not by the PEXlib server as determined by the other values of parameterization. PEXlib will generate texture coordinates for Extended or OC Context Fill Area Sets, Set of Fill Area Sets, Triangle Strips, or Quadrilateral Meshes via the utility routines PEXExtTMCoordFillAreaSetWithData, PEXExtTMCoordSetOfFillAreaSets, PEXExtTMCoordTriangleStrip, or PEXExtTMCoordQuadrilateralMesh.

    The only PEXlib primitives that can be texture-mapped are either PEXExtFillAreaSetWithData, PEXExtSetOfFillAreaSets, PEXExtTriangleStrip, and PEXExtQuadrilateralMesh; or, alternatively, PEXOCCFillArea, PEXOCCFillAreaSet, PEXOCCIndexedFillAreaSets, PEXOCCTriangleStrip, and PEXOCCQuadrilateralMesh.

    Texture coordinates created by the PEXExtTMCoordFillAreaSetWithData, PEXExtTMCoordSetOfFillAreaSets, PEXExtTMCoordTriangleStrip, and PEXExtTMCoordQuadrilateralMesh routines result in "standard" texture mapping, as opposed to reflection mapping. Standard mapping is independent of the camera, and as such, may be calculated once for each primitive. These utilities calculate the texture coordinates and store them with the primitive's vertex data. The application must reserve space within the coordinate data for the texture coordinates before these utilities are called.

    The PEXExtTMCoord* utilities require that the following information be specified:

    • projection: PEXExtTMProjectionSphereWC, PEXExtTMProjectionCylinderWC, and PEXExtTMProjectionLinearWC are supported projection objects. The primitive described in a call to one of these utilities will be conceptually projected onto the projection object (sphere, cylinder, or plane) to derive the texture coordinates.

      Figure 10-6 Projection Methods Used to Calculate Texture Coordinates

      Projection Methods Used to Calculate Texture Coordinates
    • matrix: For greater control over the positioning of texture coordinates relative to a primitive, the projection object can be transformed by the matrix in param_data for either the spherical or cylindrical projections. This can serve to change the orientation of the projection. The user may want to change the orientation of the projection to move texture seams or distortions where they will be less noticeable once the texture is mapped onto a primitive. It is often desirable to align the axis of revolution of the projection object with the natural axis of symmetry for the geometrical object (if it has one). The projection matrix is passed to the PEXExtTMCoord* utilities in the tm_coord_data parameter for explicit projections and to PEXExtCreateTMDescription for all other projections. It is recommended that the matrix contain only 3D rotations and should be noted that it is never applied to linear projections.

    • coord_source: The coordinate source to be used to compute the texture coordinates must be supplied. For cylindrical and spherical projections, a direction vector is computed using either the vertex coordinate or the vertex normal. Using one or the other may produce results that are more pleasing to the end user depending on the primitive and texture map. If PEXExtTMCoordSourceVertexNormal is selected, but normals are not supplied with the primitive's vertex data, the normals will be derived by the utility.

    • model_transform: A model coordinate transform is provided to convert from model to world coordinates, if desired. One advanced use of the model_transform, for example, is a tire modeled once but instantiated four times. The model transform for each instantiation could be specified and the texture coordinates for each model transform stored in a different location of the tire primitives' vertex lists. This would result in four sets of texture coordinates being stored with each vertex. By using the mc_transform, each tire would be properly texture mapped according to its orientation in the scene.

    • vertex_attributes: Note that an application must set the flag PEXExtGAData in the vertex_attributes parameter passed to the PEXExtTMCoord* utilities to notify PEXlib that there will be texture coordinates stored with the vertex data.

(See also “User Interface Considerations for Parameterization ”.)

Step 4: Set up Texture Mapping Lookup Tables (LUTs)

The texture mapping Lookup Tables control how a texture is positioned on an object and how the texture mapped object will appear once it is rendered. These LUTs are created, manipulated, and inquired using the standard calls, PEXCreateLookupTable, PEXGetTableInfo, PEXGetDefinedIndices, and the extended calls PEXExtSetTableEntries, PEXExtGetTableEntry, PEXExtGetTableEntries, and PEXExtFreeTableEntries.

Note that to change renderer attributes, including which Lookup Tables are associated with a renderer, the extended routine PEXExtChangeRenderer must be used.

  1. Create one or more “Coordinate-Source LUT” entries to specify how a texture is mapped onto a primitive.

    The Coordinate Source LUT specifies how a texture is placed on the geometry. Of prime importance is the orientation matrix. Each texture coordinate is transformed by this matrix before accessing the texture map. Many applications will need to provide end-user access to the orientation matrix (via a mouse or other input device) so that textures can be positioned precisely on objects. For these applications, there is no exact way to know how a texture should be oriented and user input is indispensable. For example, only the end-user knows how a texture mapped label should be oriented on a package. For other applications, particularly when mapping real-world data, the position of the texture map is intrinsic to the texture data and the user will not need to position the textures on the object.

    The orientation matrix differs from the reflection matrix used by PEXExtCreateTMDescription and PEXExtTMCoord* in that it is applied to the texture coordinates before accessing the texture map while the reflection matrix is applied to the projection object as one of the steps taken to determine the texture coordinates.

  2. Create one or more “Composition LUT ” entries to describe how texture-map data is combined with the existing color and alpha data of a primitive.

    The Composition LUT specifies how a texture map is combined with a primitive's existing color and alpha values. Supported composition techniques are Replace, Modulate, and Decal:

  3. The Replace operation overwrites the primitive's existing color with that of the texture map. Likewise, if a texture alpha is specified, the primitive's alpha is replaced. If alpha is not specified, the primitive's alpha remains unchanged.

  4. The Modulate operation multiplies each component (R, G, B) of the primitive's existing color with each component of the texture map color. If texture alpha is specified, the primitive's alpha is multiplied by the texture alpha to determine the final alpha.

  5. The Decal operation functions much like Replace when alpha is not included in the texture map, in that the texture map color replaces the primitive's color. However, alpha is set to 1.0. If, on the other hand, alpha is specified in the texture map, the following equation is used to determine the final blended color:

    • C_{out} = C_{in}\\times(1-t_a) + t_c\\times t_a

    where C_{in} is the primitive's existing color, t_a is the texture map alpha, and t_c is the texture map color.

  6. Create one or more “Sampling LUT ” entries to specify how a filtered texture map is sampled or accessed.

    The entries in the Sampling LUT define how a texture map is sampled as it is mapped onto a primitive. Several different parameters determine the sampling method:

    • When the texture coordinates for a primitive map multiple texels to a single pixel, the minification method is used to determine exactly how the texels should be used to determine the color for that pixel.

    • When the texture coordinates map one texel to multiple pixels, the magnification method determines what values should be assigned to the multiple pixels.

    • Boundary conditions t0_boundary_condition, t1_boundary_condition, and t2_boundary_condition specify how texturing should be applied when the texture coordinates select a point outside the texture map. The t0_boundary_condition is for the t0 (horizontal) coordinate, t1_boundary_condition is used for the t1 (vertical) coordinate, and t2_boundary_condition is not used.

    • The depth_sampling_bias_hint can be used to adjust which level of a texture MIP map is sampled. This results in sharpening or blurring the texture detail depending upon the new level selected in the map.

    • The t0_frequency_hint, t1_frequency_hint, and t2_frequency_hint, can be applied by advanced texture mapping users based on the actual data in a given texture map. If blurring in any one direction would be unacceptably high due to low spatial frequency, this hint may be set to a value between 0.0 and 1.0 for that direction. The affect will be to bias the texture map sampling to reduce the blurring.

  7. Create one or more “Binding LUT ” entries to associate a texture description with entries in the Coordinate Source, Composition, and Sampling LUTs. Binding LUT entries are passed to PEXExtSetActiveTextures to activate one or more textures for subsequent primitives.

Step 5: Render

  1. Set up rendering options.

    • Optionally use PEXExtSetTMSampleFrequency. The texture mapping sample frequency specifies the frequency to use when sampling texels in a texture map. The only supported value for frequency is PEXExtTMSampleFrequencyPixel, meaning that texture map texels are sampled once for each pixel.

    • Optionally use PEXExtSetTMPerspectiveCorrection to control whether to apply perspective correction. Because texture coordinates are calculated for vertices only, these coordinates must be interpolated to find the appropriate values for the points between the vertices and on the interior of the primitives. This call determines whether perspective correction is applied during interpolation.

    • Optionally use PEXExtSetTMResourceHints. This call allows the user to ask PEXlib to optimize texture mapping performance to the possible detriment of memory usage, or vice versa. It is also possible to specify a list of textures that are believed to be the ones most often used by the user. Note that as hints, the requests made by this call may or may not be followed. Although the way in which system resources are used may be affected by these resource hints, the actual image displayed will not change.

    • Enable texture mapping for subsequent primitives.

      To texture-map a primitive, the interior style must be set to PEXExtInteriorStyleTexture via PEXSetInteriorStyle. Alternatively, bundle tables may be used and PEXSetInteriorBundleIndex may be called.

  2. Activate texture(s).

    One or more textures must be activated using the call PEXExtSetActiveTextures. Backface textures can be activated by PEXExtSetBFActiveTextures. Note that the number of active textures allowed may be inquired using PEXGetImpDepConstants.

    Note that if the application needs to redefine the pipeline context, PEXExtChangePipelineContext can be called to set the texture mapping attributes perspective correction, resource hints, sample frequency, and front-face and back-face active textures.

  3. Render primitives.

    Primitives that are to be texture mapped must be rendered using either one of the routines, PEXExtFillAreaSetWithData, PEXExtSetOfFillAreaSets, PEXExtTriangleStrip, or PEXExtQuadrilateralMesh; or, alternatively, PEXOCCFillArea, PEXOCCFillAreaSet, PEXOCCIndexedFillAreaSets, PEXOCCTriangleStrip, and PEXOCCQuadrilateralMesh.

Step 6: Clean Up

  1. Free resources used by texture mapping. The routines PEXExtFreeTM and PEXExtFreeTMDescription remove the association between a resource ID and the texture map and a resource ID and texture map description, respectively. The storage held by a resource will be freed when no other resource references it.

References

  • Bier, Eric A. and Sloan, Kenneth R., Jr., "Two-Part Texture Mappings," IEEE Computer Graphics and Applications, Sept. 1986, The Computer Society, Los Alamitos, CA, pp. 40 — 53.

  • Blinn, James F. and Newell, Martin E., "Texture and Reflection in Computer Generated Images," Communications of the ACM, Vol. 19, No. 10 (Oct 1976); Association for Computing Machinery, Inc., New York, 1976, pp. 542 — 547.

  • Blinn, James F., "Simulation of Wrinkled Surfaces," ACM Computer Graphics, Vol. 12, No 3., (SIGgraph Proceedings 1978), Association for Computing Machinery, Inc., New York, 1978, pp. 286 — 292.

  • Catmull, Edwin, "A Subdivision Algorithm for Computer Display of Curved Surfaces," doctoral dissertation, University of Utah, Salt Lake City, Utah, December 1974.

  • Crow, Franklin C., "Summed-Area Tables for Texture Mapping," ACM Computer Graphics, Vol. 18, No. 3, (SIGGRAPH Proceedings 1984), Association for Computing Machinery, Inc., New York, 1984, pp. 207 — 212.

  • Glassner, Andrew S., 3D Computer Graphics - A User's Guide for Artists and Designers, Design Press, New York, 1989.

  • Heckbert, Paul S., "Survey of Texture Mapping," IEEE Computer Graphics and Applications, Nov. 1986, The Computer Society, Los Alamitos, CA, 1986, pp. 56 — 67.

  • Peachey, Darwyn R., "Solid Texture of Complex Surfaces," ACM Computer Graphics, Vol. 19, No. 3, (SIGgraph Proceedings 1985), Association for Computing Machinery, Inc., New York, 1985, pp. 279 — 286.

  • Watt, Alan and Watt, Mark, Advanced Animation and Rendering Techniques - Theory and Practice, Addison-Wesley Publishing Company, ACM Press, New York, 1992.

  • Williams, Lance, "Pyramidal Parametrics," ACM Computer Graphics, Vol. 17, No. 3, (SIGgraph Proceedings 1983), Association for Computing Machinery, Inc., New York, pp. 1 — 11.

  • Wolberg, George, Digital Image Warping' IEEE Computer Society Press, Los Alamitos, 1990.

Printable version
Privacy statement Using this site means you accept its terms Feedback to webmaster
© 1996 Hewlett-Packard Development Company, L.P.