\ .\" This man page was generated by the Netpbm tool 'makeman' from HTML source. .\" Do not hand-hack it! If you have bug fixes or improvements, please find .\" the corresponding HTML page on the Netpbm website, generate a patch .\" against that, and send it to the Netpbm maintainer. .TH "Libnetpbm Image Processing Manual" 3 "December 2003" "netpbm documentation" .PP .UR #toc Table Of Contents .UE \& .SH NAME libnetpbm_image - overview of netpbm image-processing functions .SH DESCRIPTION .PP This reference manual covers functions in the \fBlibnetpbm\fP library for processing images, using the Netpbm image formats and the \fBlibnetpbm\fP in-memory image formats. .PP For historical reasons as well as to avoid clutter, it does not cover the largely obsolete PBM, PGM, PPM, and PNM classes of \fBlibnetpbm\fP functions. For those, see .BR PBM Function Manual (3) , .BR PGM Function Manual (3) , .BR PPM Function Manual (3) , and .BR PNM Function Manual (3) . Note that you do \fInot\fP need those functions to process PBM, PGM, PPM, and PNM images. The functions in this manual are sufficient for that. .PP The PPM drawing functions are covered separately in .BR PPM Drawing Function Manual (1) . .PP For introductory and general information using \fBlibnetpbm\fP, see .BR Libnetpbm User's Guide (3) . .PP \fBlibnetpbm\fP also contains functions that are not specifically oriented toward processing image data. Read about those in the .BR Libnetpbm Utility Manual (3) . .PP To use these services, #include \fBpam.h\fP. .UN types .SH Types .PP Here are some important types that you use with \fBlibnetpbm\fP: .TP sample A sample of a Netpbm image. See the format specifications -- as an example, the red intensity of a particular pixel of a PPM image is a sample. This is an integer type. .TP tuple A tuple from a PAM image or the PAM equivalent of a PNM image. See the PAM format specification -- as an example, a pixel of a PPM image would be a tuple. A tuple is an array of samples. .TP samplen Same as \fBsample\fP, except in normalized form. This is a floating point type with a value in the range 0..1. 0 corresponds to a PAM/PNM sample value of 0. 1 corresponds to a PAM/PNM sample value equal to the image's maxval. .TP tuplen The same as \fBtuple\fP, except composed of normalized samples (\fBsamplen\fP) instead of regular samples (\fBsample\fP). .UN pamstruct .SS struct pam .PP The main argument to most of the PAM functions is the address of a \fBpam\fP structure, which is defined as follows: .nf \f(CW \fBstruct pam {\fP \fBint \fP\fIsize\fP \fBint \fP\fIlen\fP \fBFILE *\fP\fIfile \fP \fBint \fP\fIformat\fP \fBint \fP\fIplainformat\fP \fBint \fP\fIheight\fP \fBint \fP\fIwidth\fP \fBint \fP\fIdepth\fP \fBsample \fP\fImaxval\fP \fBint \fP\fIbytes_per_sample\fP \fBchar \fP\fItuple_type\fP\fB[256]\fP \fBint \fP\fIallocation_depth\fP \fBchar **\fP\fIcomment_p\fP\fB;\fP \fB}\fP \fP .fi .PP See .UR libnetpbm_ug.html#pamstruct The Libnetbm User's Guide .UE \& for information on the \fBpam\fP structure. .UN macros .SH Macros \fBPNM_MAXMAXVAL\fP is the maximum maxval that Netpbm images could historically have: 255. Many programs aren't capable of handling Netpbm images with a maxval larger than this. It's named this way for backward compatibility -- it had this name back when it was \fIthe\fP maximum maxval. .PP \fBPNM_OVERALLMAXVAL\fP is the maximum maxval that Netpbm images can have today (65535). .PP \fBPBM_FORMAT\fP, \fBRPBM_FORMAT\fP, \fBPGM_FORMAT\fP, \fBRPGM_FORMAT\fP, \fBPPM_FORMAT\fP, \fBRPPM_FORMAT\fP, and \fBPAM_FORMAT\fP are the format codes of the various Netpbm formats. \fBRPBM_FORMAT\fP is the raw PBM format and \fBPBM_FORMAT\fP is the plain PBM format, and so on. See the \fIformat\fP member of .UR libnetpbm_ug.html#pamstruct the \fBpam\fP structure .UE \&. .PP \fBPAM_FORMAT_TYPE(\fP\fIformat\fP\fB)\fP gives the type of a format, given the format code. The types of formats are PBM, PGM, PPM, and PAM and macros for the type codes are, respectively, PBM_TYPE, PGM_TYPE, PPM_TYPE, and PAM_TYPE. Note that there are more format codes then there are format types because there are different format codes for the plain and raw subformats of each format. .UN functions .SH Functions .PP These interfaces are declared in \fBpam.h\fP. .UN memory .SS Memory Management .B Synopsis .PP \fBtuple ** pnm_allocpamarray(\fP \fBstruct pam *\fP\fIpamP\fP\fB);\fP .PP \fBtuple * pnm_allocpamrow(\fP \fBstruct pam *\fP\fIpamP\fP\fB);\fP .PP \fBvoid pnm_freepamarray(\fP \fBtuple **\fP\fItuplearray\fP\fB,\fP \fBstruct pam *\fP\fIpamP\fP\fB);\fP .PP \fBvoid pnm_freepamrow(\fP \fBtuple *\fP\fItuplerow\fP\fB);\fP .PP \fBtuple * allocpamtuple(\fP \fBstruct pam *\fP\fIpamP\fP\fB);\fP .PP \fBvoid pnm_freepamtuple(\fP \fBtuple \fP\fItuple\fP \fB);\fP .PP \fBtuplen * pnm_allocpamrown(\fP \fBstruct pam *\fP\fIpamP\fP\fB);\fP .PP \fBvoid pnm_freepamrown(\fP \fBtuplen *\fP\fItuplenrow\fP\fB);\fP .B Description .PP \fBpnm_allocpamarray()\fP allocates space for an array of tuples. \fBpnm_freepamarray()\fP frees an array space allocated by \fBpnm_allocpamarray()\fP or \fBpnm_readpam()\fP. .PP \fBpnm_allocpamrow() \fP allocates space for a row of a PAM image, in basic form. \fBpnm_freepamrow()\fP frees it. .PP \fBpnm_allocpamrown()\fP is the same as \fBpnm_allocpamrow()\fP except that it allocates space for a PAM row in the normalized form. \fBpnm_freepamrown()\fP is similarly like \fBpnm_freepamrow\fP. .UN reading .SS Reading Netpbm Files .B Synopsis .PP \fBvoid pnm_readpaminit(\fP \fBFILE *\fP\fIfile\fP\fB,\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBint \fP\fIsize\fP\fB);\fP .PP \fBvoid pnm_readpamrow(\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBtuple *\fP\fItuplerow\fP\fB);\fP .PP \fBtuple ** pnm_readpam(\fP \fBFILE *\fP\fIfile\fP\fB,\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBint \fP\fIsize\fP\fB);\fP .PP \fBvoid pnm_readpamrown(\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBtuplen *\fP\fItuplenrow\fP\fB);\fP .B Description .PP \fBpnm_readpaminit()\fP reads the header of a Netpbm image. .PP See above for a general description of the \fIpamP\fP argument. .PP \fBpnm_readpaminit()\fP returns the information from the header in the \fB*\fP\fIpamP\fP structure. It does not require any members of \fB*\fP\fIpamP\fP through \fBtuple_type\fP to be set at invocation, and sets all of those members. It expects all members after \fBtuple_type\fP to be meaningful. .PP \fIsize\fP is the size of the \fB*\fP\fIpamP\fP structure as understood by the program processing the image. \fBpnm_readpaminit()\fP does not attempt to use or set any members of the structure beyond that. The point of this argument is that the definition of the structure may change over time, with additional fields being added to the end. This argument allows \fBpnm_readpaminit\fP to distinguish between a new program that wants to exploit the additional features and an old program that cannot (or a new program that just doesn't want to deal with the added complexity). At a minimum, this size must contain the members up through \fBtuple_type\fP. You should use the \fBPAM_STRUCT_SIZE\fP macro to compute this argument. E.g. \fBPAM_STRUCT_SIZE(tuple_type)\fP. \fBPAM_STRUCT_SIZE\fP was introduced in Netpbm 10.23 (July 2004). In older Netpbm, you can just use sizeof(), but then your code is \fInot\fP forward compatible at the source code level with newer libnetpbm (because when you compile it with newer libnetpbm header files, you'll be saying your structure contains all the new members that have been invented, but your code doesn't actually initialize them). So you might want to compute a proper size yourself. .PP The function expects to find the image file positioned to the start of the header and leaves it positioned to the start of the raster. .PP \fBpnm_readpamrow()\fP reads a row of the raster from a Netpbm image file. It expects all of the members of the \fB*pamP\fP structure to be set upon invocation and does not modify any of them. It expects to find the file positioned to the start of the row in question in the raster and leaves it positioned just after it. It returns the row as the array of tuples \fItuplerow\fP, which must already have its column pointers set up so that it forms a C 2-dimensional array. The leftmost tuple is Element 0 of this array. .PP \fBpnm_readpam()\fP reads an entire image from a PAM or PNM image file and allocates the space in which to return the raster. It expects to find the file positioned to the first byte of the image and leaves it positioned just after the image. .PP The function does not require \fB*\fP\fIpamP\fP to have any of its members set and sets them all. \fIsize\fP is the storage size in bytes of the \fB*\fP\fIpamP\fP structure, normally \fBsizeof(struct pam)\fP. .PP The return value is a newly allocated array of the rows of the image, with the top row being Element 0 of the array. Each row is represented as \fBpnm_readpamrow()\fP would return. .PP The return value is also effectively a 3-dimensional C array of samples, with the dimensions corresponding to the height, width, and depth of the image, in that order. .PP \fBpnm_readpam()\fP combines the functions of \fBpnm_allocpamarray()\fP, \fBpnm_readpaminit()\fP, and iterations of \fBpnm_readpamrow()\fP. It may require more dynamic storage than you can afford. .PP \fBpnm_readpamrown()\fP is like \fBpnm_readpamrow()\fP except that it returns the row contents in normalized form (composed of normalized tuples (\fBtuplen\fP) instead of basic form (\fBtuple\fP). .PP \fBpnm_readpaminit()\fP and \fBpnm_readpam\fP abort the program with a message to Standard Error if the PAM or PNM image header is not syntactically valid, including if it contains a number too large to be processed using the system's normal data structures (to wit, a number that won't fit in a C 'int'). .UN writing .SS Writing Netpbm Files .B Synopsis .PP \fBvoid pnm_writepaminit(\fP \fBstruct pam *\fP\fIpamP\fP\fB);\fP .PP \fBvoid pnm_writepamrow(\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBconst tuple *\fP\fItuplerow\fP\fB);\fP .PP \fBvoid pnm_writepam(\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBconst tuple * const *\fP\fItuplearray\fP\fB);\fP .PP \fBvoid pnm_writepamrown(\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBconst tuplen *\fP\fItuplerown\fP\fB);\fP .PP \fBvoid pnm_formatpamrow(\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBconst tuple *\fP\fItuplerow\fP \fBunsigned char * const \fP\fIoutbuf\fP\fB,\fP \fBunsigned int * const \fP\fIrowSizeP\fP \fB);\fP .B Description .PP \fBpnm_writepaminit()\fP writes the header of a PAM or PNM image and computes some of the fields of the pam structure. .PP See above for a description of the \fIpamP\fP argument. .PP The following members of the \fB*\fP\fIpamP\fP structure must be set upon invocation to tell the function how and what to write. \fBsize\fP, \fBlen\fP, \fBfile\fP, \fBformat\fP, \fBheight\fP, \fBwidth\fP, \fBdepth\fP, \fBmaxval\fP. Furthermore, if \fBformat\fP is \fBPAM_FORMAT\fP, \fBtuple_type\fP must be set and if \fBformat\fP is \fInot\fP \fBPAM_FORMAT\fP, \fBplainformat\fP must be set. .PP \fBpnm_writepaminit()\fP sets the \fBbytes_per_sample\fP member based on the information supplied. .PP \fBpnm_writepamrow()\fP writes a row of the raster into a PAM or PNM image file. It expects to find the file positioned where the row should start and leaves it positioned just after the row. The function requires all the elements of \fB*\fP\fIpamP\fP to be set upon invocation and doesn't modify them. .PP \fItuplerow\fP is an array of tuples representing the row. The leftmost tuple is Element 0 of this array. .PP \fBpnm_writepam()\fP writes an entire PAM or PNM image to a PAM or PNM image file. It expects to find the file positioned to where the image should start and leaves it positioned just after the image. .PP The members of the \fB*\fP\fIpamP\fP structure that must be set up invocation, and their meanings, is the same as for \fBpnm_writepaminit\fP. .PP \fBpnm_writepam()\fP sets the \fBbytes_per_sample\fP member based on the information supplied. .PP \fItuplearray\fP is an array of rows such that you would pass to \fBpnm_writepamrow()\fP, with the top row being Element 0 of the array. .PP \fBpnm_writepam()\fP combines the functions of \fBpnm_writepaminit()\fP, and iterations of \fBpnm_writepamrow()\fP. Its raster input may be more storage than you can afford. .PP \fBpnm_writepamrown()\fP is like \fBpnm_writepamrow()\fP except that it takes the row contents in normalized form (composed of normalized tuples (\fBtuplen\fP) instead of basic form (\fBtuple\fP). .PP \fBpnm_formatpamrow()\fP is like \fBpnm_writepamrow()\fP, except that instead of writing a row to a file, it places the same bytes that would go in the file in a buffer you supply. There isn't an equivalent function to construct an image header; i.e. there is no analog to \fBpnm_writepaminit()\fP. But the header format, particularly for PAM, is so simple that you can easily build it yourself with standard C library string functions. .PP \fBpnm_formatpamrow()\fP was new in Netpbm 10.25 (October 2004). .UN transform .SS Transforming Pixels .B Synopsis .PP \fBvoid pnm_YCbCrtuple(\fP \fBtuple \fP\fItuple\fP\fB,\fP \fBdouble *\fP\fIYP\fP\fB,\fP \fBdouble *\fP\fICrP\fP\fB,\fP \fBdouble *\fP\fICbP\fP\fB);\fP .PP \fBvoid pnm_YCbCr_to_rgbtuple(\fP \fBconst struct pam * const \fP\fIpamP\fP\fB,\fP \fBtuple const \fP\fItuple\fP\fB,\fP \fBdouble const \fP\fIY\fP\fB,\fP \fBdouble const \fP\fICb\fP\fB,\fP \fBdouble const \fP\fICr\fP\fB,\fP \fBint * const \fP\fIoverflowP\fP\fB);\fP .PP \fBextern double pnm_lumin_factor[3];\fP .PP \fBvoid pnm_normalizetuple(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBtuple const \fP\fItuple\fP\fB,\fP \fBtuplen const \fP\fItuplen\fP\fB);\fP .PP \fBvoid pnm_unnormalizetuple(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBtuplen const \fP\fItuplen\fP\fB,\fP \fBtuple const \fP\fItuple\fP\fB);\fP .PP \fBvoid pnm_normalizeRow(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBconst tuple * const \fP\fItuplerow\fP\fB,\fP \fBpnm_transformMap * const \fP\fItransform\fP\fB,\fP \fBtuplen * const \fP\fItuplenrow\fP\fB);\fP .PP \fBvoid pnm_unnormalizeRow(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBconst tuplen * const \fP\fItuplenrow\fP\fB,\fP \fBpnm_transformMap * const \fP\fItransform\fP\fB,\fP \fBtuple * const \fP\fItuplerow\fP\fB);\fP .PP \fBvoid pnm_gammarown(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBtuplen * const \fP\fIrow\fP\fB);\fP .PP \fBvoid pnm_ungammarown(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBtuplen * const \fP\fIrow\fP\fB);\fP .PP \fBvoid pnm_applyopacityrown(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBtuplen * const \fP\fItuplenrow\fP\fB);\fP .PP \fBvoid pnm_unapplyopacityrown(\fP \fBstruct pam * const \fP\fIpamP\fP\fB,\fP \fBtuplen * const \fP\fItuplenrow\fP\fB);\fP .PP \fBpnm_transformMap * pnm_creategammatransform(\fP \fBconst struct pam * const \fP\fIpamP\fP\fB);\fP .PP \fBvoid pnm_freegammatransform(\fP \fBconst pnm_transformMap * const \fP\fItransform\fP\fB,\fP \fBconst struct pam * const \fP\fIpamP\fP\fB);\fP .PP \fBpnm_transformMap * pnm_createungammatransform(\fP \fBconst struct pam * const \fP\fIpamP\fP\fB);\fP .PP \fBvoid pnm_freeungammatransform(\fP \fBconst pnm_transformMap * const \fP\fItransform\fP\fB,\fP \fBconst struct pam * const \fP\fIpamP\fP\fB);\fP .B Description .PP \fBpnm_YCbCrtuple()\fP returns the Y/Cb/Cr luminance/chrominance representation of the color represented by the input tuple, assuming that the tuple is an RGB color representation (which is the case if it was read from a PPM image). The output components are based on the same scale (maxval) as the input tuple, but are floating point nonetheless to avoid losing information because of rounding. Divide them by the maxval to get normalized [0..1] values. .PP \fBpnm_YCbCr_to_rgbtuple()\fP does the reverse. \fIpamP\fP indicates the maxval for the returned \fItuple\fP, and the \fIY\fP, \fICb\fP, and \fICr\fP arguments are of the same scale. .PP It is possible for \fIY\fP, \fICb\fP, and \fICr\fP to describe a color that cannot be represented in RGB form. In that case, \fBpnm_YCbCr_to_rgbtuple()\fP chooses a color as close as possible (by clipping each component to 0 and the maxval) and sets *overflowP true. It otherwise sets *overflowP false. \fBpnm_lumin_factor[]\fP is the factors (weights) one uses to compute the intensity of a color (according to some standard -- I don't know which). pnm_lumin_factor[0] is for the red component, [1] is for the green, and [2] is for the blue. They add up to 1. .PP \fBpnm_gammarown()\fP and \fBpnm_ungammarown()\fP apply and unapply gamma correction to a row of an image using the same transformation as .UR libpm.html#gamma \fBpm_gamma709()\fP and \fBpm_ungamma709()\fP .UE \&. Note that these operate on a row of normalized tuples (\fBtuplen\fP, not \fBtuple\fP). .PP \fBpnm_applyopacityrown()\fP reduces the intensity of samples in accordance with the opacity plane of an image. The opacity plane, if it exists, tells how much of the light from that pixel should show when the image is composed with another image. You use \fBpnm_applyopacityrown()\fP in preparation for doing such a composition. For example, if the opacity plane says that the left half of the image is 50% opaque and the right half 100% opaque, \fBpnm_applyopacityrown()\fP will reduce the intensity of each sample of each tuple (pixel) in the left half of the image by 50%, and leave the rest alone. .PP If the image does not have an opacity plane (i.e. its tuple type is not one that \fBlibnetpbm\fP recognizes as having an opacity plane), \fBpnm_applyopacityrown()\fP does nothing (which is the same as assuming opacity 100%). The tuple types that \fBlibnetpbm\fP recognizes as having opacity are \fBRGB_ALPHA\fP and \fBGRAYSCALE_ALPHA\fP. .PP \fBpnm_unapplyopacityrown()\fP does the reverse. It assumes the intensities are already reduced according to the opacity plane, and raises back to normal. .PP \fBpnm_applyopacityrown()\fP works on (takes as input and produces as output) \fInormalized\fP, \fIintensity-proportional\fP tuples. That means you will typically read the row from the image file with \fBpnm_readpamrown()\fP and then gamma-correct it with \fBpnm_ungammarown()\fP, and then do \fBpnm_applyopacityrown()\fP. You then manipulate the row further (perhaps add it with other rows you've processed similarly), then do \fBpnm_unapplyopacityrown()\fP, then \fBpnm_gammarown()\fP, then \fBpnm_writepamrown()\fP. .PP \fBpnm_applyopacityrown()\fP and \fBpnm_unapplyopacityrown()\fP were new in Netpbm 10.25 (October 2004). .PP \fBpnm_normalizetuple()\fP and \fBpnm_unnormalizetuple()\fP convert between a \fBtuple\fP data type and a \fBtuplen\fP data type. The former represents a sample value using the same unsigned integer that is in the PAM image, while the latter represents a sample value as a number scaled by the maxval to the range 0..1. I.e. \fBpnm_normalizetuple()\fP divides every sample value by the maxval and \fBpnm_unnormalizetuple()\fP multiples every sample by the maxval. .PP \fBpnm_normalizeRow()\fP and \fBpnm_unnormalizeRow()\fP do the same thing on an entire tuple row, but also have an extra feature: You can specify a transform function to be applied in addition. Typically, this is a gamma transform function. You can of course more easily apply your transform function separately from normalizing, but doing it all at once is usually way faster. Why? Because you can use a lookup table that is indexed by an integer on one side and produces a floating point number on the other. To do it separately, you'd either have to do floating point arithmetic on the normalized value or do the transform on the integer values and lose a lot of precision. .PP If you don't have any transformation to apply, just specify \fBNULL\fP for the \fItransform\fP argument and the function will just normalize (i.e. divide or multiply by the maxval). .PP Here's an example of doing a transformation. The example composes two images together, something that has to be done with intensity-linear sample values. .nf pnm_transformMap * const transform1 = pnm_createungammatransform(&inpam1); pnm_transformMap * const transform2 = pnm_createungammatransform(&inpam2); pnm_transformMap * const transformOut = pnm_creategammatransform(&outpam); pnm_readpamrow(&inpam1, inrow1); pnm_readpamrow(&inpam2, inrow2); pnm_normalizeRow(&inpam1, inrow1, transform1, normInrow1); pnm_normalizeRow(&inpam2, inrow2, transform2, normInrow2); for (col = 0; col < outpam.width; ++col) normOutrow[col] = (normInrow1[col] + normInrow2[col])/2; pnm_unnormalizeRow(&outpam, normOutrow, transformOut, outrow); pnm_writepamrow(&outpam, outrow); .fi .PP To specify a transform, you must create a special \fBpnm_transformMap\fP object and pass it as the \fItransform\fP argument. Typically, your transform is a gamma transformation because you want to work in intensity-proportional sample values and the PAM image format uses gamma-adjusted ones. In that case, just use \fBpnm_creategammatransform()\fP and \fBpnm_createungammatransform()\fP to create this object and don't worry about what's inside it. .PP \fBpnm_creategammatransform()\fP and \fBpnm_createungammatransform()\fP create objects that you use with \fBpnm_normalizeRow()\fP and \fBpnm_unnormalizeRow()\fP as described above. The created object describes a transform that applies or reverses the ITU-R Recommendation BT.709 gamma adjustment that is used in PAM visual images and normalizes or unnormalizes the sample values. \fBpnm_freegammatransform()\fP and \fBpnm_freeungammatransform()\fP destroy the objects. .UN misc .SS Miscellaneous .B Synopsis .PP \fBvoid pnm_checkpam(\fP \fBstruct pam *\fP\fIpamP\fP\fB,\fP \fBconst enum pm_check_type \fP\fIcheck_type\fP\fB,\fP \fBenum pm_check_code *\fP\fIretvalP\fP\fB);\fP .PP \fBvoid pnm_nextimage(\fP \fBFILE *\fP\fIfile\fP\fB,\fP \fBint * const \fP\fIeofP\fP\fB);\fP .B Description .PP \fBpnm_checkpam()\fP checks for the common file integrity error where the file is the wrong size to contain the raster, according to the information in the header. .PP \fBpnm_nextimage()\fPpositions a Netpbm image input file to the next image in it (so that a subsequent \fBpnm_readpaminit()\fP reads its header).