GNU Unifont 15.1.04
Pan-Unicode font with complete Unicode Plane 0 coverage and partial coverage of higher planes
unibmpbump.c File Reference

unibmpbump - Adjust a Microsoft bitmap (.bmp) file that was created by unihex2png but converted to .bmp More...

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
Include dependency graph for unibmpbump.c:

Go to the source code of this file.

Macros

#define VERSION   "1.0"
 Version of this program.
 
#define MAX_COMPRESSION_METHOD   13
 Maximum supported compression method.
 

Functions

int main (int argc, char *argv[])
 The main function.
 
unsigned get_bytes (FILE *infp, int nbytes)
 Get from 1 to 4 bytes, inclusive, from input file.
 
void regrid (unsigned *image_bytes)
 After reading in the image, shift it.
 

Detailed Description

unibmpbump - Adjust a Microsoft bitmap (.bmp) file that was created by unihex2png but converted to .bmp

Author
Paul Hardy, unifoundry <at> unifoundry.com

This program shifts the glyphs in a bitmap file to adjust an original PNG file that was saved in BMP format. This is so the result matches the format of a unihex2bmp image. This conversion then lets unibmp2hex decode the result.

Synopsis: unibmpbump [-iin_file.bmp] [-oout_file.bmp]

Definition in file unibmpbump.c.

Macro Definition Documentation

◆ MAX_COMPRESSION_METHOD

#define MAX_COMPRESSION_METHOD   13

Maximum supported compression method.

Definition at line 40 of file unibmpbump.c.

◆ VERSION

#define VERSION   "1.0"

Version of this program.

Definition at line 38 of file unibmpbump.c.

Function Documentation

◆ get_bytes()

unsigned get_bytes ( FILE *  infp,
int  nbytes 
)

Get from 1 to 4 bytes, inclusive, from input file.

Parameters
[in]infpPointer to input file.
[in]nbytesNumber of bytes to read, from 1 to 4, inclusive.
Returns
The unsigned 1 to 4 bytes in machine native endian format.

Definition at line 487 of file unibmpbump.c.

487 {
488 int i;
489 unsigned char inchar[4];
490 unsigned inword;
491
492 for (i = 0; i < nbytes; i++) {
493 if (fread (&inchar[i], 1, 1, infp) != 1) {
494 inchar[i] = 0;
495 }
496 }
497 for (i = nbytes; i < 4; i++) inchar[i] = 0;
498
499 inword = ((inchar[3] & 0xFF) << 24) | ((inchar[2] & 0xFF) << 16) |
500 ((inchar[1] & 0xFF) << 8) | (inchar[0] & 0xFF);
501
502 return inword;
503}
Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char *  argv[] 
)

The main function.

Parameters
[in]argcThe count of command line arguments.
[in]argvPointer to array of command line arguments.
Returns
This program exits with status EXIT_SUCCESS.

Definition at line 50 of file unibmpbump.c.

50 {
51
52 /*
53 Values preserved from file header (first 14 bytes).
54 */
55 char file_format[3]; /* "BM" for original Windows format */
56 unsigned filesize; /* size of file in bytes */
57 unsigned char rsvd_hdr[4]; /* 4 reserved bytes */
58 unsigned image_start; /* byte offset of image in file */
59
60 /*
61 Values preserved from Device Independent Bitmap (DIB) Header.
62
63 The DIB fields below are in the standard 40-byte header. Version
64 4 and version 5 headers have more information, mainly for color
65 information. That is skipped over, because a valid glyph image
66 is just monochrome.
67 */
68 int dib_length; /* in bytes, for parsing by header version */
69 int image_width = 0; /* Signed image width */
70 int image_height = 0; /* Signed image height */
71 int num_planes = 0; /* number of planes; must be 1 */
72 int bits_per_pixel = 0; /* for palletized color maps (< 2^16 colors) */
73 /*
74 The following fields are not in the original spec, so initialize
75 them to 0 so we can correctly parse an original file format.
76 */
77 int compression_method=0; /* 0 --> uncompressed RGB/monochrome */
78 int image_size = 0; /* 0 is a valid size if no compression */
79 int hres = 0; /* image horizontal resolution */
80 int vres = 0; /* image vertical resolution */
81 int num_colors = 0; /* Number of colors for pallettized images */
82 int important_colors = 0; /* Number of significant colors (0 or 2) */
83
84 int true_colors = 0; /* interpret num_colors, which can equal 0 */
85
86 /*
87 Color map. This should be a monochrome file, so only two
88 colors are stored.
89 */
90 unsigned char color_map[2][4]; /* two of R, G, B, and possibly alpha */
91
92 /*
93 The monochrome image bitmap, stored as a vector 544 rows by
94 72*8 columns.
95 */
96 unsigned image_bytes[544*72];
97
98 /*
99 Flags for conversion & I/O.
100 */
101 int verbose = 0; /* Whether to print file info on stderr */
102 unsigned image_xor = 0x00; /* Invert (= 0xFF) if color 0 is not black */
103
104 /*
105 Temporary variables.
106 */
107 int i, j, k; /* loop variables */
108
109 /* Compression type, for parsing file */
110 char *compression_type[MAX_COMPRESSION_METHOD + 1] = {
111 "BI_RGB", /* 0 */
112 "BI_RLE8", /* 1 */
113 "BI_RLE4", /* 2 */
114 "BI_BITFIELDS", /* 3 */
115 "BI_JPEG", /* 4 */
116 "BI_PNG", /* 5 */
117 "BI_ALPHABITFIELDS", /* 6 */
118 "", "", "", "", /* 7 - 10 */
119 "BI_CMYK", /* 11 */
120 "BI_CMYKRLE8", /* 12 */
121 "BI_CMYKRLE4", /* 13 */
122 };
123
124 /* Standard unihex2bmp.c header for BMP image */
125 unsigned standard_header [62] = {
126 /* 0 */ 0x42, 0x4d, 0x3e, 0x99, 0x00, 0x00, 0x00, 0x00,
127 /* 8 */ 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x28, 0x00,
128 /* 16 */ 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x20, 0x02,
129 /* 24 */ 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
130 /* 32 */ 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0xc4, 0x0e,
131 /* 40 */ 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
132 /* 48 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 /* 56 */ 0x00, 0x00, 0xff, 0xff, 0xff, 0x00
134 };
135
136 unsigned get_bytes (FILE *, int);
137 void regrid (unsigned *);
138
139 char *infile="", *outfile=""; /* names of input and output files */
140 FILE *infp, *outfp; /* file pointers of input and output files */
141
142 /*
143 Process command line arguments.
144 */
145 if (argc > 1) {
146 for (i = 1; i < argc; i++) {
147 if (argv[i][0] == '-') { /* this is an option argument */
148 switch (argv[i][1]) {
149 case 'i': /* name of input file */
150 infile = &argv[i][2];
151 break;
152 case 'o': /* name of output file */
153 outfile = &argv[i][2];
154 break;
155 case 'v': /* verbose output */
156 verbose = 1;
157 break;
158 case 'V': /* print version & quit */
159 fprintf (stderr, "unibmpbump version %s\n\n", VERSION);
160 exit (EXIT_SUCCESS);
161 break;
162 case '-': /* see if "--verbose" */
163 if (strcmp (argv[i], "--verbose") == 0) {
164 verbose = 1;
165 }
166 else if (strcmp (argv[i], "--version") == 0) {
167 fprintf (stderr, "unibmpbump version %s\n\n", VERSION);
168 exit (EXIT_SUCCESS);
169 }
170 break;
171 default: /* if unrecognized option, print list and exit */
172 fprintf (stderr, "\nSyntax:\n\n");
173 fprintf (stderr, " unibmpbump ");
174 fprintf (stderr, "-i<Input_File> -o<Output_File>\n\n");
175 fprintf (stderr, "-v or --verbose gives verbose output");
176 fprintf (stderr, " on stderr\n\n");
177 fprintf (stderr, "-V or --version prints version");
178 fprintf (stderr, " on stderr and exits\n\n");
179 fprintf (stderr, "\nExample:\n\n");
180 fprintf (stderr, " unibmpbump -iuni0101.bmp");
181 fprintf (stderr, " -onew-uni0101.bmp\n\n");
182 exit (EXIT_SUCCESS);
183 }
184 }
185 }
186 }
187
188 /*
189 Make sure we can open any I/O files that were specified before
190 doing anything else.
191 */
192 if (strlen (infile) > 0) {
193 if ((infp = fopen (infile, "r")) == NULL) {
194 fprintf (stderr, "Error: can't open %s for input.\n", infile);
195 exit (EXIT_FAILURE);
196 }
197 }
198 else {
199 infp = stdin;
200 }
201 if (strlen (outfile) > 0) {
202 if ((outfp = fopen (outfile, "w")) == NULL) {
203 fprintf (stderr, "Error: can't open %s for output.\n", outfile);
204 exit (EXIT_FAILURE);
205 }
206 }
207 else {
208 outfp = stdout;
209 }
210
211
212 /* Read bitmap file header */
213 file_format[0] = get_bytes (infp, 1);
214 file_format[1] = get_bytes (infp, 1);
215 file_format[2] = '\0'; /* Terminate string with null */
216
217 /* Read file size */
218 filesize = get_bytes (infp, 4);
219
220 /* Read Reserved bytes */
221 rsvd_hdr[0] = get_bytes (infp, 1);
222 rsvd_hdr[1] = get_bytes (infp, 1);
223 rsvd_hdr[2] = get_bytes (infp, 1);
224 rsvd_hdr[3] = get_bytes (infp, 1);
225
226 /* Read Image Offset Address within file */
227 image_start = get_bytes (infp, 4);
228
229 /*
230 See if this looks like a valid image file based on
231 the file header first two bytes.
232 */
233 if (strncmp (file_format, "BM", 2) != 0) {
234 fprintf (stderr, "\nInvalid file format: not file type \"BM\".\n\n");
235 exit (EXIT_FAILURE);
236 }
237
238 if (verbose) {
239 fprintf (stderr, "\nFile Header:\n");
240 fprintf (stderr, " File Type: \"%s\"\n", file_format);
241 fprintf (stderr, " File Size: %d bytes\n", filesize);
242 fprintf (stderr, " Reserved: ");
243 for (i = 0; i < 4; i++) fprintf (stderr, " 0x%02X", rsvd_hdr[i]);
244 fputc ('\n', stderr);
245 fprintf (stderr, " Image Start: %d. = 0x%02X = 0%05o\n\n",
246 image_start, image_start, image_start);
247 } /* if (verbose) */
248
249 /*
250 Device Independent Bitmap (DIB) Header: bitmap information header
251 ("BM" format file DIB Header is 12 bytes long).
252 */
253 dib_length = get_bytes (infp, 4);
254
255 /*
256 Parse one of three versions of Device Independent Bitmap (DIB) format:
257
258 Length Format
259 ------ ------
260 12 BITMAPCOREHEADER
261 40 BITMAPINFOHEADER
262 108 BITMAPV4HEADER
263 124 BITMAPV5HEADER
264 */
265 if (dib_length == 12) { /* BITMAPCOREHEADER format -- UNTESTED */
266 image_width = get_bytes (infp, 2);
267 image_height = get_bytes (infp, 2);
268 num_planes = get_bytes (infp, 2);
269 bits_per_pixel = get_bytes (infp, 2);
270 }
271 else if (dib_length >= 40) { /* BITMAPINFOHEADER format or later */
272 image_width = get_bytes (infp, 4);
273 image_height = get_bytes (infp, 4);
274 num_planes = get_bytes (infp, 2);
275 bits_per_pixel = get_bytes (infp, 2);
276 compression_method = get_bytes (infp, 4); /* BI_BITFIELDS */
277 image_size = get_bytes (infp, 4);
278 hres = get_bytes (infp, 4);
279 vres = get_bytes (infp, 4);
280 num_colors = get_bytes (infp, 4);
281 important_colors = get_bytes (infp, 4);
282
283 /* true_colors is true number of colors in image */
284 if (num_colors == 0)
285 true_colors = 1 << bits_per_pixel;
286 else
287 true_colors = num_colors;
288
289 /*
290 If dib_length > 40, the format is BITMAPV4HEADER or
291 BITMAPV5HEADER. As this program is only designed
292 to handle a monochrome image, we can ignore the rest
293 of the header but must read past the remaining bytes.
294 */
295 for (i = 40; i < dib_length; i++) (void)get_bytes (infp, 1);
296 }
297
298 if (verbose) {
299 fprintf (stderr, "Device Independent Bitmap (DIB) Header:\n");
300 fprintf (stderr, " DIB Length: %9d bytes (version = ", dib_length);
301
302 if (dib_length == 12) fprintf (stderr, "\"BITMAPCOREHEADER\")\n");
303 else if (dib_length == 40) fprintf (stderr, "\"BITMAPINFOHEADER\")\n");
304 else if (dib_length == 108) fprintf (stderr, "\"BITMAPV4HEADER\")\n");
305 else if (dib_length == 124) fprintf (stderr, "\"BITMAPV5HEADER\")\n");
306 else fprintf (stderr, "unknown)");
307 fprintf (stderr, " Bitmap Width: %6d pixels\n", image_width);
308 fprintf (stderr, " Bitmap Height: %6d pixels\n", image_height);
309 fprintf (stderr, " Color Planes: %6d\n", num_planes);
310 fprintf (stderr, " Bits per Pixel: %6d\n", bits_per_pixel);
311 fprintf (stderr, " Compression Method: %2d --> ", compression_method);
312 if (compression_method <= MAX_COMPRESSION_METHOD) {
313 fprintf (stderr, "%s", compression_type [compression_method]);
314 }
315 /*
316 Supported compression method values:
317 0 --> uncompressed RGB
318 11 --> uncompressed CMYK
319 */
320 if (compression_method == 0 || compression_method == 11) {
321 fprintf (stderr, " (no compression)");
322 }
323 else {
324 fprintf (stderr, "Image uses compression; this is unsupported.\n\n");
325 exit (EXIT_FAILURE);
326 }
327 fprintf (stderr, "\n");
328 fprintf (stderr, " Image Size: %5d bytes\n", image_size);
329 fprintf (stderr, " Horizontal Resolution: %5d pixels/meter\n", hres);
330 fprintf (stderr, " Vertical Resolution: %5d pixels/meter\n", vres);
331 fprintf (stderr, " Number of Colors: %5d", num_colors);
332 if (num_colors != true_colors) {
333 fprintf (stderr, " --> %d", true_colors);
334 }
335 fputc ('\n', stderr);
336 fprintf (stderr, " Important Colors: %5d", important_colors);
337 if (important_colors == 0)
338 fprintf (stderr, " (all colors are important)");
339 fprintf (stderr, "\n\n");
340 } /* if (verbose) */
341
342 /*
343 Print Color Table information for images with pallettized colors.
344 */
345 if (bits_per_pixel <= 8) {
346 for (i = 0; i < 2; i++) {
347 color_map [i][0] = get_bytes (infp, 1);
348 color_map [i][1] = get_bytes (infp, 1);
349 color_map [i][2] = get_bytes (infp, 1);
350 color_map [i][3] = get_bytes (infp, 1);
351 }
352 /* Skip remaining color table entries if more than 2 */
353 while (i < true_colors) {
354 (void) get_bytes (infp, 4);
355 i++;
356 }
357
358 if (color_map [0][0] >= 128) image_xor = 0xFF; /* Invert colors */
359 }
360
361 if (verbose) {
362 fprintf (stderr, "Color Palette [R, G, B, %s] Values:\n",
363 (dib_length <= 40) ? "reserved" : "Alpha");
364 for (i = 0; i < 2; i++) {
365 fprintf (stderr, "%7d: [", i);
366 fprintf (stderr, "%3d,", color_map [i][0] & 0xFF);
367 fprintf (stderr, "%3d,", color_map [i][1] & 0xFF);
368 fprintf (stderr, "%3d,", color_map [i][2] & 0xFF);
369 fprintf (stderr, "%3d]\n", color_map [i][3] & 0xFF);
370 }
371 if (image_xor == 0xFF) fprintf (stderr, "Will Invert Colors.\n");
372 fputc ('\n', stderr);
373
374 } /* if (verbose) */
375
376
377 /*
378 Check format before writing output file.
379 */
380 if (image_width != 560 && image_width != 576) {
381 fprintf (stderr, "\nUnsupported image width: %d\n", image_width);
382 fprintf (stderr, "Width should be 560 or 576 pixels.\n\n");
383 exit (EXIT_FAILURE);
384 }
385
386 if (image_height != 544) {
387 fprintf (stderr, "\nUnsupported image height: %d\n", image_height);
388 fprintf (stderr, "Height should be 544 pixels.\n\n");
389 exit (EXIT_FAILURE);
390 }
391
392 if (num_planes != 1) {
393 fprintf (stderr, "\nUnsupported number of planes: %d\n", num_planes);
394 fprintf (stderr, "Number of planes should be 1.\n\n");
395 exit (EXIT_FAILURE);
396 }
397
398 if (bits_per_pixel != 1) {
399 fprintf (stderr, "\nUnsupported number of bits per pixel: %d\n",
400 bits_per_pixel);
401 fprintf (stderr, "Bits per pixel should be 1.\n\n");
402 exit (EXIT_FAILURE);
403 }
404
405 if (compression_method != 0 && compression_method != 11) {
406 fprintf (stderr, "\nUnsupported compression method: %d\n",
407 compression_method);
408 fprintf (stderr, "Compression method should be 1 or 11.\n\n");
409 exit (EXIT_FAILURE);
410 }
411
412 if (true_colors != 2) {
413 fprintf (stderr, "\nUnsupported number of colors: %d\n", true_colors);
414 fprintf (stderr, "Number of colors should be 2.\n\n");
415 exit (EXIT_FAILURE);
416 }
417
418
419 /*
420 If we made it this far, things look okay, so write out
421 the standard header for image conversion.
422 */
423 for (i = 0; i < 62; i++) fputc (standard_header[i], outfp);
424
425
426 /*
427 Image Data. Each row must be a multiple of 4 bytes, with
428 padding at the end of each row if necessary.
429 */
430 k = 0; /* byte number within the binary image */
431 for (i = 0; i < 544; i++) {
432 /*
433 If original image is 560 pixels wide (not 576), add
434 2 white bytes at beginning of row.
435 */
436 if (image_width == 560) { /* Insert 2 white bytes */
437 image_bytes[k++] = 0xFF;
438 image_bytes[k++] = 0xFF;
439 }
440 for (j = 0; j < 70; j++) { /* Copy next 70 bytes */
441 image_bytes[k++] = (get_bytes (infp, 1) & 0xFF) ^ image_xor;
442 }
443 /*
444 If original image is 560 pixels wide (not 576), skip
445 2 padding bytes at end of row in file because we inserted
446 2 white bytes at the beginning of the row.
447 */
448 if (image_width == 560) {
449 (void) get_bytes (infp, 2);
450 }
451 else { /* otherwise, next 2 bytes are part of the image so copy them */
452 image_bytes[k++] = (get_bytes (infp, 1) & 0xFF) ^ image_xor;
453 image_bytes[k++] = (get_bytes (infp, 1) & 0xFF) ^ image_xor;
454 }
455 }
456
457
458 /*
459 Change the image to match the unihex2bmp.c format if original wasn't
460 */
461 if (image_width == 560) {
462 regrid (image_bytes);
463 }
464
465 for (i = 0; i < 544 * 576 / 8; i++) {
466 fputc (image_bytes[i], outfp);
467 }
468
469
470 /*
471 Wrap up.
472 */
473 fclose (infp);
474 fclose (outfp);
475
476 exit (EXIT_SUCCESS);
477}
#define VERSION
Version of this program.
Definition: unibmpbump.c:38
void regrid(unsigned *image_bytes)
After reading in the image, shift it.
Definition: unibmpbump.c:514
#define MAX_COMPRESSION_METHOD
Maximum supported compression method.
Definition: unibmpbump.c:40
unsigned get_bytes(FILE *infp, int nbytes)
Get from 1 to 4 bytes, inclusive, from input file.
Definition: unibmpbump.c:487
Here is the call graph for this function:

◆ regrid()

void regrid ( unsigned *  image_bytes)

After reading in the image, shift it.

This function adjusts the input image from an original PNG file to match unihex2bmp.c format.

Parameters
[in,out]image_bytesThe pixels in an image.

Definition at line 514 of file unibmpbump.c.

514 {
515 int i, j, k; /* loop variables */
516 int offset;
517 unsigned glyph_row; /* one grid row of 32 pixels */
518 unsigned last_pixel; /* last pixel in a byte, to preserve */
519
520 /* To insert "00" after "U+" at top of image */
521 char zero_pattern[16] = {
522 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42,
523 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00
524 };
525
526 /* This is the horizontal grid pattern on glyph boundaries */
527 unsigned hgrid[72] = {
528 /* 0 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
529 /* 8 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00,
530 /* 16 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00,
531 /* 24 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00,
532 /* 32 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00,
533 /* 40 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00,
534 /* 48 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00,
535 /* 56 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00,
536 /* 64 */ 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, 0x81, 0x00
537 };
538
539
540 /*
541 First move "U+" left and insert "00" after it.
542 */
543 j = 15; /* rows are written bottom to top, so we'll decrement j */
544 for (i = 543 - 8; i > 544 - 24; i--) {
545 offset = 72 * i;
546 image_bytes [offset + 0] = image_bytes [offset + 2];
547 image_bytes [offset + 1] = image_bytes [offset + 3];
548 image_bytes [offset + 2] = image_bytes [offset + 4];
549 image_bytes [offset + 3] = image_bytes [offset + 4] =
550 ~zero_pattern[15 - j--] & 0xFF;
551 }
552
553 /*
554 Now move glyph bitmaps to the right by 8 pixels.
555 */
556 for (i = 0; i < 16; i++) { /* for each glyph row */
557 for (j = 0; j < 16; j++) { /* for each glyph column */
558 /* set offset to lower left-hand byte of next glyph */
559 offset = (32 * 72 * i) + (9 * 72) + (4 * j) + 8;
560 for (k = 0; k < 16; k++) { /* for each glyph row */
561 glyph_row = (image_bytes [offset + 0] << 24) |
562 (image_bytes [offset + 1] << 16) |
563 (image_bytes [offset + 2] << 8) |
564 (image_bytes [offset + 3]);
565 last_pixel = glyph_row & 1; /* preserve border */
566 glyph_row >>= 4;
567 glyph_row &= 0x0FFFFFFE;
568 /* Set left 4 pixels to white and preserve last pixel */
569 glyph_row |= 0xF0000000 | last_pixel;
570 image_bytes [offset + 3] = glyph_row & 0xFF;
571 glyph_row >>= 8;
572 image_bytes [offset + 2] = glyph_row & 0xFF;
573 glyph_row >>= 8;
574 image_bytes [offset + 1] = glyph_row & 0xFF;
575 glyph_row >>= 8;
576 image_bytes [offset + 0] = glyph_row & 0xFF;
577 offset += 72; /* move up to next row in current glyph */
578 }
579 }
580 }
581
582 /* Replace horizontal grid with unihex2bmp.c grid */
583 for (i = 0; i <= 16; i++) {
584 offset = 32 * 72 * i;
585 for (j = 0; j < 72; j++) {
586 image_bytes [offset + j] = hgrid [j];
587 }
588 }
589
590 return;
591}
Here is the caller graph for this function: