001/* 002 003*/ 004 005package renderer.framebuffer; 006 007import java.awt.Color; 008import java.io.FileInputStream; 009import java.io.FileOutputStream; 010import java.io.FileNotFoundException; 011import java.io.IOException; 012import java.awt.image.BufferedImage; 013import javax.imageio.ImageIO; 014 015/** 016 A {@code FrameBuffer} represents a two-dimensional array of pixel data. 017 The pixel data is stored as a one dimensional array in row-major order. 018 The first row of data should be displayed as the top row of pixels 019 in the image. 020<p> 021 A "viewport" is a two-dimensional sub array of a {@code FrameBuffer}. 022 A {@code FrameBuffer} has one current viewport. The current viewport 023 is represented by its upper-left-hand corner and its lower-right-hand 024 corner. 025<p> 026 {@code FrameBuffer} and viewport coordinates act like Java 027 {@link java.awt.Graphics2D} coordinates; the positive x direction is 028 to the right and the positive y direction is downward. 029*/ 030public class FrameBuffer 031{ 032 private Color bgColorFB; // the framebuffer's background color 033 private int width; // the framebuffer's width 034 private int height; // the framebuffer's height 035 public int[] pixel_buffer; // contains each pixel's color data for a rendered frame 036 037 // Coordinates of the current viewport within the framebuffer. 038 private int vp_ul_x; // upper-left-hand corner 039 private int vp_ul_y; 040 private int vp_lr_x; // lower-right-hand corner 041 private int vp_lr_y; 042 private Color bgColorVP; // the viewport's background color 043 044 /** 045 Construct a {@code FrameBuffer} with the given dimensions. 046 Initialize the framebuffer to hold all black pixels. 047 048 @param w width of the {@code FrameBuffer}. 049 @param h height of the {@code FrameBuffer}. 050 */ 051 public FrameBuffer(int w, int h) 052 { 053 this(w, h, Color.black); 054 } 055 056 057 /** 058 Construct a {@code FrameBuffer} with the given dimensions and 059 initialize the {@code FrameBuffer} to the given {@link Color}. 060 061 @param w width of the {@code FrameBuffer}. 062 @param h height of the {@code FrameBuffer}. 063 @param c background {@link Color} for the {@code FrameBuffer} 064 */ 065 public FrameBuffer(int w, int h, Color c) 066 { 067 this.width = w; 068 this.height = h; 069 070 // Set the default viewport (to be the whole framebuffer). 071 this.vp_ul_x = 0; 072 this.vp_ul_y = 0; 073 this.vp_lr_x = this.width - 1; 074 this.vp_lr_y = this.height - 1; 075 076 // Create the pixel buffer. 077 pixel_buffer = new int[this.width * this.height]; 078 079 // Initialize the pixel buffer. 080 this.bgColorFB = c; 081 this.bgColorVP = c; 082 clearFB(); 083 } 084 085 086 /** 087 Construct a {@code FrameBuffer} from a PPM image file. 088 <p> 089 This can be used to initialize a {@code FrameBuffer} 090 with a background image. 091 092 @param inputFileName must name a PPM image file with magic number P6. 093 */ 094 public FrameBuffer(String inputFileName) 095 { 096 // Read the pixel data in a PPM file. 097 // http://stackoverflow.com/questions/2693631/read-ppm-file-and-store-it-in-an-array-coded-with-c 098 try 099 { 100 FileInputStream fis = new FileInputStream(inputFileName); 101 102 // Read image format string "P6". 103 String magicNumber = ""; 104 char c = (char)fis.read(); 105 while (c != '\n') 106 { 107 magicNumber += c; 108 c = (char)fis.read(); 109 } 110 if (! magicNumber.trim().startsWith("P6")) 111 { 112 System.err.printf("ERROR! Improper PPM number in file %s\n", inputFileName); 113 System.exit(-1); 114 } 115 116 c = (char)fis.read(); 117 if ( '#' == c ) // read (and discard) IrfanView comment 118 { 119 while (c != '\n') 120 { 121 c = (char)fis.read(); 122 } 123 c = (char)fis.read(); 124 } 125 126 // Read image dimensions. 127 String widthDim = ""; 128 while (c != ' ' && c != '\n') 129 { 130 widthDim += c; 131 c = (char)fis.read(); 132 } 133 134 String heightDim = ""; 135 c = (char)fis.read(); 136 while (c != '\n') 137 { 138 heightDim += c; 139 c = (char)fis.read(); 140 } 141 142 fis.close(); 143 144 this.width = Integer.parseInt(widthDim.trim()); 145 this.height = Integer.parseInt(heightDim.trim()); 146 147 // Set the default viewport (to be the whole framebuffer). 148 vp_ul_x = 0; 149 vp_ul_y = 0; 150 vp_lr_x = this.width - 1; 151 vp_lr_y = this.height - 1; 152 153 // Create the pixel buffer. 154 pixel_buffer = new int[this.width * this.height]; 155 156 // Initialize the pixel buffer. 157 this.bgColorFB = Color.black; 158 this.bgColorVP = Color.black; 159 clearFB(); 160 161 setViewport(vp_ul_x, vp_ul_y, inputFileName); 162 } 163 catch (IOException e) 164 { 165 System.err.printf("ERROR! Could not read %s\n", inputFileName); 166 e.printStackTrace(System.err); 167 System.exit(-1); 168 } 169 } 170 171 172 /** 173 Get the width of the {@code FrameBuffer}. 174 175 @return this {@code FrameBuffer}'s width 176 */ 177 public int getWidthFB() 178 { 179 return this.width; 180 } 181 182 183 /** 184 Get the height of the {@code FrameBuffer}. 185 186 @return this {@code FrameBuffer}'s height 187 */ 188 public int getHeightFB() 189 { 190 return this.height; 191 } 192 193 194 /** 195 Get the background {@link Color} of the {@code FrameBuffer}. 196 197 @return this {@code FrameBuffer}'s background {@link Color} 198 */ 199 public Color getBgColorFB() 200 { 201 return this.bgColorFB; 202 } 203 204 205 /** 206 Set the background {@link Color} of the {@code FrameBuffer}. 207 208 @param c this {@code FrameBuffer}'s new background {@link Color} 209 */ 210 public void setBgColorFB(Color c) 211 { 212 this.bgColorFB = c; 213 } 214 215 216 /** 217 Clear the {@code FrameBuffer} using its background color. 218 */ 219 public void clearFB() 220 { 221 clearFB(bgColorFB); 222 } 223 224 225 /** 226 Clear the {@code FrameBuffer} using the given {@link Color}. 227 228 @param c {@link Color} to clear {@code FrameBuffer} with 229 */ 230 public void clearFB(Color c) 231 { 232 for (int y = 0; y < height; y++) 233 { 234 for (int x = 0; x < width; x++) 235 { 236 setPixelFB(x, y, c); 237 } 238 } 239 } 240 241 242 /** 243 Get the {@link Color} of the pixel with coordinates 244 (x,y) in the framebuffer. 245 246 @param x horizontal coordinate within the {@code FrameBuffer} 247 @param y vertical coordinate within the {@code FrameBuffer} 248 @return the {@link Color} of the current pixel at the given pixel coordinates 249 */ 250 public Color getPixelFB(int x, int y) 251 { 252 int index = (y*width + x); 253 try 254 { 255 int rgb = pixel_buffer[index]; 256 return new Color(rgb); 257 } 258 catch(ArrayIndexOutOfBoundsException e) 259 { 260 System.err.println("FrameBuffer: Bad pixel coordinate (" + x + ", " + y +")"); 261 //e.printStackTrace(System.err); 262 return Color.black; 263 } 264 } 265 266 267 /** 268 Set the {@link Color} of the pixel with coordinates 269 (x,y) in the {@code FrameBuffer}. 270 271 @param x horizontal coordinate within the {@code FrameBuffer} 272 @param y vertical coordinate within the {@code FrameBuffer} 273 @param c {@link Color} for the pixel at the given pixel coordinates 274 */ 275 public void setPixelFB(int x, int y, Color c) 276 { 277 int index = (y*width + x); 278 try 279 { 280 pixel_buffer[index] = c.getRGB(); 281 } 282 catch(ArrayIndexOutOfBoundsException e) 283 { 284 System.err.println("FrameBuffer: Bad pixel coordinate (" + x + ", " + y +")"); 285 //e.printStackTrace(System.err); 286 } 287 } 288 289 290 /** 291 Set the coordinates, within the {@code FrameBuffer}, of 292 the viewport's upper-left-hand corner, width, and height. 293 (Using upper-left-hand corner, width, and height is 294 like Java's {@link java.awt.Rectangle} class and 295 {@link java.awt.Graphics#drawRect} method.) 296 297 @param vp_ul_x upper left hand x-coordinate of new viewport rectangle 298 @param vp_ul_y upper left hand y-coordinate of new viewport rectangle 299 @param width viewport's width 300 @param height viewport's height 301 */ 302 public void setViewport(int vp_ul_x, int vp_ul_y, int width, int height) 303 { 304 this.vp_ul_x = vp_ul_x; 305 this.vp_ul_y = vp_ul_y; 306 this.vp_lr_x = vp_ul_x + width - 1; 307 this.vp_lr_y = vp_ul_y + height - 1; 308 } 309 310 311 /** 312 Set the viewport to be the whole {@code FrameBuffer}, 313 */ 314 public void setViewport() 315 { 316 this.vp_ul_x = 0; 317 this.vp_ul_y = 0; 318 this.vp_lr_x = this.width - 1; 319 this.vp_lr_y = this.height - 1; 320 } 321 322 323 /** 324 Create a viewport from a {@code FrameBuffer}. 325 <p> 326 The size of the viewport will be the size of the source {@code FrameBuffer}. 327 328 @param vp_ul_x upper left hand x-coordinate of new viewport rectangle 329 @param vp_ul_y upper left hand y-coordinate of new viewport rectangle 330 @param sourceFB {@code FrameBuffer} to use as the source of the pixel data.. 331 */ 332 public void setViewport(int vp_ul_x, int vp_ul_y, FrameBuffer sourceFB) 333 { 334 int vpWidth = sourceFB.width; 335 int vpHeight = sourceFB.height; 336 337 this.vp_ul_x = vp_ul_x; 338 this.vp_ul_y = vp_ul_y; 339 this.vp_lr_x = vp_ul_x + vpWidth - 1; 340 this.vp_lr_y = vp_ul_y + vpHeight - 1; 341 342 // Read pixel data, one pixel at a time, from the source FrameBuffer. 343 for (int y = 0; y < vpHeight; y++) 344 { 345 for (int x = 0; x < vpWidth; x++) 346 { 347 this.setPixelVP(x, y, sourceFB.getPixelFB(x,y)); 348 } 349 } 350 } 351 352 353 /** 354 Create a viewport from a PPM image file. 355 <p> 356 The size of the viewport will be the size of the image. 357 <p> 358 This can be used to initialize a viewport with a background image. 359 360 @param vp_ul_x upper left hand x-coordinate of new viewport rectangle 361 @param vp_ul_y upper left hand y-coordinate of new viewport rectangle 362 @param inputFileName must name a PPM image file with magic number P6. 363 */ 364 public void setViewport(int vp_ul_x, int vp_ul_y, String inputFileName) 365 { 366 // Read the pixel data in a PPM file. 367 // http://stackoverflow.com/questions/2693631/read-ppm-file-and-store-it-in-an-array-coded-with-c 368 try 369 { 370 FileInputStream fis = new FileInputStream(inputFileName); 371 372 // Read image format string "P6". 373 String magicNumber = ""; 374 char c = (char)fis.read(); 375 while (c != '\n') 376 { 377 magicNumber += c; 378 c = (char)fis.read(); 379 } 380 if (! magicNumber.trim().startsWith("P6")) 381 { 382 System.err.printf("ERROR! Improper PPM number in file %s\n", inputFileName); 383 System.exit(-1); 384 } 385 386 c = (char)fis.read(); 387 if ( '#' == c ) // read (and discard) IrfanView comment 388 { 389 while (c != '\n') 390 { 391 c = (char)fis.read(); 392 } 393 c = (char)fis.read(); 394 } 395 396 // Read image dimensions. 397 String widthDim = ""; 398 while (c != ' ' && c != '\n') 399 { 400 widthDim += c; 401 c = (char)fis.read(); 402 } 403 404 String heightDim = ""; 405 c = (char)fis.read(); 406 while (c != '\n') 407 { 408 heightDim += c; 409 c = (char)fis.read(); 410 } 411 412 // Read image rgb dimensions (which we don't use). 413 c = (char)fis.read(); 414 while (c != '\n') 415 { 416 c = (char)fis.read(); 417 } 418 419 int vpWidth = Integer.parseInt(widthDim.trim()); 420 int vpHeight = Integer.parseInt(heightDim.trim()); 421 422 this.vp_ul_x = vp_ul_x; 423 this.vp_ul_y = vp_ul_y; 424 this.vp_lr_x = vp_ul_x + vpWidth - 1; 425 this.vp_lr_y = vp_ul_y + vpHeight - 1; 426 427 // Create a data array. 428 byte[] pixelData = new byte[3]; 429 // Read pixel data, one pixel at a time, from the PPM file. 430 for (int y = 0; y < vpHeight; y++) 431 { 432 for (int x = 0; x < vpWidth; x++) 433 { 434 if ( fis.read(pixelData, 0, 3) != 3 ) 435 { 436 System.err.printf("ERROR! Could not load %s\n", inputFileName); 437 System.exit(-1); 438 } 439 int r = pixelData[0]; 440 int g = pixelData[1]; 441 int b = pixelData[2]; 442 if (r < 0) r = 256+r; // convert from signed byte to unsigned byte 443 if (g < 0) g = 256+g; 444 if (b < 0) b = 256+b; 445 setPixelVP(x, y, new Color(r, g, b)); 446 } 447 } 448 fis.close(); 449 } 450 catch (IOException e) 451 { 452 System.err.printf("ERROR! Could not read %s\n", inputFileName); 453 e.printStackTrace(System.err); 454 System.exit(-1); 455 } 456 } 457 458 459 /** 460 Get the width of the viewport. 461 462 @return width of the current viewport rectangle 463 */ 464 public int getWidthVP() 465 { 466 return vp_lr_x - vp_ul_x + 1; 467 } 468 469 470 /** 471 Get the height of the viewport. 472 473 @return height of the current viewport rectangle 474 */ 475 public int getHeightVP() 476 { 477 return vp_lr_y - vp_ul_y + 1; 478 } 479 480 481 /** 482 Get the upper left hand corner of the viewport. 483 484 @return upper left hand corner of current viewport rectangle 485 */ 486 public java.awt.Point getLocationVP() 487 { 488 return new java.awt.Point(vp_ul_x, vp_ul_y); 489 } 490 491 492 /** 493 Get the background {@link Color} of the viewport. 494 495 @return background {@link Color} of current viewport 496 */ 497 public Color getBgColorVP() 498 { 499 return this.bgColorVP; 500 } 501 502 503 /** 504 Set the background {@link Color} of the viewport. 505 506 @param c background {@link Color} of current viewport 507 */ 508 public void setBgColorVP(Color c) 509 { 510 this.bgColorVP = c; 511 } 512 513 514 /** 515 Clear the viewport using its background color. 516 */ 517 public void clearVP() 518 { 519 clearVP(bgColorVP); 520 } 521 522 523 /** 524 Clear the viewport using the given {@link Color}. 525 526 @param c {@link Color} to clear current viewport with 527 */ 528 public void clearVP(Color c) 529 { 530 int wVP = getWidthVP(); 531 int hVP = getHeightVP(); 532 533 for (int y = 0; y < hVP; y++) 534 { 535 for (int x = 0; x < wVP; x++) 536 { 537 setPixelVP(x, y, c); 538 } 539 } 540 } 541 542 543 /** 544 Get the {@link Color} of the pixel with coordinates 545 (x,y) relative to the current viewport. 546 547 @param x horizontal coordinate within the current viewport 548 @param y vertical coordinate within the current viewport 549 @return the {@link Color} of the current pixel at the given viewport coordinates 550 */ 551 public Color getPixelVP(int x, int y) 552 { 553 return getPixelFB(vp_ul_x + x, vp_ul_y + y); 554 } 555 556 557 /** 558 Set the {@link Color} of the pixel with coordinates 559 (x,y) relative to the current viewport. 560 561 @param x horizontal coordinate within the current viewport 562 @param y vertical coordinate within the current viewport 563 @param c {@link Color} for the pixel at the given viewport coordinates 564 */ 565 public void setPixelVP(int x, int y, Color c) 566 { 567 setPixelFB(vp_ul_x + x, vp_ul_y + y, c); 568 } 569 570 571 /** 572 Create a new {@code FrameBuffer} containing the pixel data 573 from this {@code FrameBuffer}'s current viewport rectangle. 574 575 @return {@code FrameBuffer} object holding pixel data from the current viewport rectangle 576 */ 577 public FrameBuffer convertVP2FB() 578 { 579 int wVP = this.getWidthVP(); 580 int hVP = this.getHeightVP(); 581 582 FrameBuffer vp_fb = new FrameBuffer( wVP, hVP ); 583 vp_fb.bgColorFB = this.bgColorVP; 584 585 // Copy the current viewport into the new framebuffer's pixel buffer. 586 for (int y = 0; y < hVP; y++) 587 { 588 for (int x = 0; x < wVP; x++) 589 { 590 vp_fb.setPixelFB( x, y, this.getPixelVP(x, y) ); 591 } 592 } 593 594 return vp_fb; 595 } 596 597 598 /** 599 Create a new {@code FrameBuffer} containing the pixel data 600 from just the red plane of this {@code FrameBuffer}. 601 <p> 602 The new {@code FrameBuffer} will have the same viewport as this 603 {@code FrameBuffer}. 604 605 @return {@code FrameBuffer} object holding just red pixel data from this {@code FrameBuffer} 606 */ 607 public FrameBuffer convertRed2FB() 608 { 609 FrameBuffer red_fb = new FrameBuffer(this.width, this.height); 610 red_fb.bgColorFB = this.bgColorFB; 611 red_fb.bgColorVP = this.bgColorVP; 612 red_fb.vp_ul_x = this.vp_ul_x; 613 red_fb.vp_ul_y = this.vp_ul_y; 614 red_fb.vp_lr_x = this.vp_lr_x; 615 red_fb.vp_lr_y = this.vp_lr_y; 616 617 // Copy the framebuffer's red values into the new framebuffer's pixel buffer. 618 for (int y = 0; y < this.height; y++) 619 { 620 for (int x = 0; x < this.width; x++) 621 { 622 Color c = new Color(this.getPixelFB(x, y).getRed(), 0, 0); 623 red_fb.setPixelFB(x, y, c); 624 } 625 } 626 627 return red_fb; 628 } 629 630 631 /** 632 Create a new {@code FrameBuffer} containing the pixel data 633 from just the green plane of this {@code FrameBuffer}. 634 <p> 635 The new {@code FrameBuffer} will have the same viewport as this 636 {@code FrameBuffer}. 637 638 @return {@code FrameBuffer} object holding just green pixel data from this {@code FrameBuffer} 639 */ 640 public FrameBuffer convertGreen2FB() 641 { 642 FrameBuffer green_fb = new FrameBuffer(this.width, this.height); 643 green_fb.bgColorFB = this.bgColorFB; 644 green_fb.bgColorVP = this.bgColorVP; 645 green_fb.vp_ul_x = this.vp_ul_x; 646 green_fb.vp_ul_y = this.vp_ul_y; 647 green_fb.vp_lr_x = this.vp_lr_x; 648 green_fb.vp_lr_y = this.vp_lr_y; 649 650 // Copy the framebuffer's green values into the new framebuffer's pixel buffer. 651 for (int y = 0; y < this.height; y++) 652 { 653 for (int x = 0; x < this.width; x++) 654 { 655 Color c = new Color(0, this.getPixelFB(x, y).getGreen(), 0); 656 green_fb.setPixelFB(x, y, c); 657 } 658 } 659 660 return green_fb; 661 } 662 663 664 /** 665 Create a new {@code FrameBuffer} containing the pixel data 666 from just the blue plane of this {@code FrameBuffer}. 667 <p> 668 The new {@code FrameBuffer} will have the same viewport as this 669 {@code FrameBuffer}. 670 671 @return {@code FrameBuffer} object holding just blue pixel data from this {@code FrameBuffer} 672 */ 673 public FrameBuffer convertBlue2FB() 674 { 675 FrameBuffer blue_fb = new FrameBuffer(this.width, this.height); 676 blue_fb.bgColorFB = this.bgColorFB; 677 blue_fb.bgColorVP = this.bgColorVP; 678 blue_fb.vp_ul_x = this.vp_ul_x; 679 blue_fb.vp_ul_y = this.vp_ul_y; 680 blue_fb.vp_lr_x = this.vp_lr_x; 681 blue_fb.vp_lr_y = this.vp_lr_y; 682 683 // Copy the framebuffer's blue values into the new framebuffer's pixel buffer. 684 for (int y = 0; y < this.height; y++) 685 { 686 for (int x = 0; x < this.width; x++) 687 { 688 Color c = new Color(0, 0, this.getPixelFB(x, y).getBlue()); 689 blue_fb.setPixelFB(x, y, c); 690 } 691 } 692 693 return blue_fb; 694 } 695 696 697 /** 698 Write this {@code FrameBuffer} to the specified PPM file. 699 <p> 700 <a href="https://en.wikipedia.org/wiki/Netpbm_format" target="_top"> 701 https://en.wikipedia.org/wiki/Netpbm_format</a> 702 703 @param filename name of PPM image file to hold {@code FrameBuffer} data 704 */ 705 public void dumpFB2File(String filename) 706 { 707 dumpPixels2File(0, 0, width-1, height-1, filename); 708 } 709 710 711 /** 712 Write the viewport to the specified PPM file. 713 <p> 714 <a href="https://en.wikipedia.org/wiki/Netpbm_format" target="_top"> 715 https://en.wikipedia.org/wiki/Netpbm_format</a> 716 717 @param filename name of PPM image file to hold viewport data 718 */ 719 public void dumpVP2File(String filename) 720 { 721 dumpPixels2File(vp_ul_x, vp_ul_y, vp_lr_x, vp_lr_y, filename); 722 } 723 724 725 /** 726 <p> 727 Write a rectangular sub array of pixels from this {@code FrameBuffer} 728 to the specified PPM file. 729 <p> 730 <a href="https://en.wikipedia.org/wiki/Netpbm_format#PPM_example" target="_top"> 731 https://en.wikipedia.org/wiki/Netpbm_format#PPM_example</a> 732 <p> 733<a href="http://stackoverflow.com/questions/2693631/read-ppm-file-and-store-it-in-an-array-coded-with-c" target="_top"> 734 http://stackoverflow.com/questions/2693631/read-ppm-file-and-store-it-in-an-array-coded-with-c</a> 735 736 @param ul_x upper left hand x-coordinate of pixel data rectangle 737 @param ul_y upper left hand y-coordinate of pixel data rectangle 738 @param lr_x lower right hand x-coordinate of pixel data rectangle 739 @param lr_y lower right hand y-coordinate of pixel data rectangle 740 @param filename name of PPM image file to hold pixel data 741 */ 742 public void dumpPixels2File(int ul_x, int ul_y, int lr_x, int lr_y, String filename) 743 { 744 int p_width = lr_x - ul_x + 1; 745 int p_height = lr_y - ul_y + 1; 746 747 FileOutputStream fos = null; 748 try // open the file 749 { 750 fos = new FileOutputStream(filename); 751 } 752 catch (FileNotFoundException e) 753 { 754 System.err.printf("ERROR! Could not open file %s\n", filename); 755 e.printStackTrace(System.err); 756 System.exit(-1); 757 } 758 //System.err.printf("Created file %s\n", filename); 759 760 try // write data to the file 761 { 762 // write the PPM header information first 763 fos.write( ("P6\n" + p_width + " " + p_height + "\n" + 255 + "\n").getBytes() ); 764 765 // write the pixel data to the file 766 byte[] temp = new byte[p_width*3]; // array to hold one row of data 767 for (int n = 0; n < p_height; n++) 768 { // write one row of pixels at a time, 769 770 // read from the top row of the data buffer 771 // down towards the bottom row 772 for (int i = 0; i < temp.length; i+=3) 773 { 774 int rgb = pixel_buffer[((ul_y+n)*width + ul_x) + i/3]; 775 Color c = new Color(rgb); 776 temp[i + 0] = (byte)(c.getRed()); 777 temp[i + 1] = (byte)(c.getGreen()); 778 temp[i + 2] = (byte)(c.getBlue()); 779 } 780 /* 781 // read from the bottom row of the data buffer 782 // up towards the top row 783 for (int i = 0; i < temp.length; i+=3) 784 { 785 int rgb = pixel_buffer[((lr_y-n)*width + ul_x) + i/3]; 786 Color c = new Color(rgb); 787 temp[i + 0] = (byte)(c.getRed()); 788 temp[i + 1] = (byte)(c.getGreen()); 789 temp[i + 2] = (byte)(c.getBlue()); 790 } 791 */ 792 fos.write(temp); // write one row of data 793 } 794 } 795 catch (IOException e) 796 { 797 System.err.printf("ERROR! Could not write to file %s\n", filename); 798 e.printStackTrace(System.err); 799 System.exit(-1); 800 } 801 802 try 803 { 804 fos.close(); 805 } 806 catch (IOException e) 807 { 808 System.err.printf("ERROR! Could not close file %s\n", filename); 809 e.printStackTrace(System.err); 810 System.exit(-1); 811 } 812 }//dumpPixels2File() 813 814 815 /** 816 Write this {@code FrameBuffer} to the specified image file 817 using the specified file format. 818 819 @param filename name of the image file to hold framebuffer data 820 @param formatName informal name of the image format 821 */ 822 public void dumpFB2File(String filename, String formatName) 823 { 824 dumpPixels2File(0, 0, width-1, height-1, filename, formatName); 825 } 826 827 828 /** 829 Write the viewport to the specified image file 830 using the specified file format. 831 832 @param filename name of the image file to hold viewport data 833 @param formatName informal name of the image format 834 */ 835 public void dumpVP2File(String filename, String formatName) 836 { 837 dumpPixels2File(vp_ul_x, vp_ul_y, vp_lr_x, vp_lr_y, filename, formatName); 838 } 839 840 841 /** 842 <p> 843 Write a rectangular sub array of pixels from this {@code FrameBuffer} 844 to the specified image file using the specified file format. 845 <p> 846 Use the static method {@link ImageIO#getWriterFormatNames} 847 to find out what informal image format names can be used 848 (for example, png, gif, jpg, bmp). 849 850 @param ul_x upper left hand x-coordinate of pixel data rectangle 851 @param ul_y upper left hand y-coordinate of pixel data rectangle 852 @param lr_x lower right hand x-coordinate of pixel data rectangle 853 @param lr_y lower right hand y-coordinate of pixel data rectangle 854 @param filename name of the image file to hold pixel data 855 @param formatName informal name of the image format 856 */ 857 public void dumpPixels2File(int ul_x, int ul_y, int lr_x, int lr_y, String filename, String formatName) 858 { 859 int p_width = lr_x - ul_x + 1; 860 int p_height = lr_y - ul_y + 1; 861 862 FileOutputStream fos = null; 863 try // open the file 864 { 865 fos = new FileOutputStream(filename); 866 } 867 catch (FileNotFoundException e) 868 { 869 System.err.printf("ERROR! Could not open file %s\n", filename); 870 e.printStackTrace(System.err); 871 System.exit(-1); 872 } 873 //System.err.printf("Created file %s\n", filename); 874 875 BufferedImage bi = new BufferedImage(p_width, p_height, BufferedImage.TYPE_INT_RGB); 876 for (int n = 0; n < p_height; n++) 877 { 878 for (int i = 0; i < p_width; i++) 879 { 880 int rgb = pixel_buffer[((ul_y+n)*width + ul_x) + i]; 881 bi.setRGB(i, n, rgb); 882 } 883 } 884 try 885 { 886 ImageIO.write(bi, formatName, fos); 887 } 888 catch (IOException e) 889 { 890 System.err.printf("ERROR! Could not write to file %s\n", filename); 891 e.printStackTrace(System.err); 892 System.exit(-1); 893 } 894 895 try 896 { 897 fos.close(); 898 } 899 catch (IOException e) 900 { 901 System.err.printf("ERROR! Could not close file %s\n", filename); 902 e.printStackTrace(System.err); 903 System.exit(-1); 904 } 905 }//dumpPixels2File() 906 907 908 909 /** 910 A simple test of the {@code FrameBuffer} class. 911 <p> 912 It fills the framebuffer with a test pattern. 913 */ 914 public void fbTest() 915 { 916 for (int y = 0; y < this.height; y++) 917 { 918 for (int x = 0; x < this.width; x++) 919 { 920 int gray = (x|y)%255; 921 setPixelFB(x, y, new Color(gray, gray, gray)); 922 } 923 } 924 }//fbTest() 925 926 927 /** 928 A simple test of the viewport. 929 <p> 930 It fills the viewport with a test pattern. 931 */ 932 public void vpTest() 933 { 934 for (int y = 0; y < this.getHeightVP(); y++) 935 { 936 for (int x = 0; x < this.getWidthVP(); x++) 937 { 938 int gray = (x|y)%255; 939 setPixelVP(x, y, new Color(gray, gray, gray)); 940 } 941 } 942 }//vpTest() 943 944 945 /** 946 A {@code main()} method for testing the {@code FrameBuffer} class. 947 948 @param args array of command-line arguments 949 */ 950 public static void main(String[] args) 951 { 952 int w = 512; 953 int h = 512; 954 FrameBuffer fb = new FrameBuffer(w, h); 955 fb.fbTest(); // fill the framebuffer with a test pattern 956 fb.dumpFB2File("test01.ppm"); 957 958 fb.setViewport(64, 64, 192, 320); // 192 by 320 959 fb.clearVP( Color.red ); 960 for (int i = 0; i < 512; i++) 961 fb.setPixelFB(128, i, Color.blue); 962 for (int i = 0; i < 192; i++) 963 fb.setPixelVP(i, i, Color.green); 964 965 fb.dumpFB2File("test02.ppm"); 966 fb.dumpVP2File("test03.ppm"); 967 fb.dumpPixels2File(32, 256-64, 511-64, 255+64, "test04.ppm"); // 416 by 128 968 969 fb.setViewport(80, 80, 160, 160); // 160 by 160 970 fb.vpTest(); // fill the viewport with a test pattern 971 fb.dumpFB2File("test05.ppm"); 972 973 FrameBuffer fb2 = new FrameBuffer("test05.ppm"); 974 fb2.dumpFB2File("test06.ppm"); 975 976 fb.convertRed2FB().dumpFB2File("test07.ppm"); 977 fb.convertGreen2FB().dumpFB2File("test08.ppm"); 978 fb.convertBlue2FB().dumpFB2File("test09.ppm"); 979 fb.convertBlue2FB().convertVP2FB().dumpFB2File("test10.ppm"); 980 981 FrameBuffer fb3 = new FrameBuffer(600, 600); 982 fb3.clearFB(Color.orange); 983 fb3.setViewport(44, 44, "test05.ppm"); 984 fb3.dumpFB2File("test11.ppm"); 985 fb3.setViewport(86, 86, fb3.convertVP2FB()); 986 fb3.dumpFB2File("test12.ppm"); 987 fb3.dumpFB2File("test12.png", "png"); 988 fb3.dumpFB2File("test12.gif", "gif"); 989 fb3.dumpFB2File("test12.jpg", "jpg"); 990 fb3.dumpFB2File("test12.bmp", "bmp"); 991 992 // list the image file formats supported by the runtime 993 for (String s : ImageIO.getWriterFormatNames()) System.out.println(s); 994 995 }//main() 996}