modules/graphics/src/main/java/com/sun/javafx/iio/bmp/BMPImageLoaderFactory.java

Print this page




  67         int ch4 = in.read();
  68         if ((ch1 | ch2 | ch3 | ch4) < 0) {
  69             throw new EOFException();
  70         }
  71         return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
  72     }
  73 
  74     public final void skipBytes(int n) throws IOException {
  75         int m = (int)in.skip(n);
  76         if (m < n) {
  77             throw new EOFException();
  78         }
  79     }
  80 }
  81 
  82 final class BitmapInfoHeader {
  83 
  84     static final int BIH_SIZE = 40;
  85     static final int BIH4_SIZE = 108;
  86     static final int BIH5_SIZE = 124;






  87 
  88     final int    biSize;
  89     final int    biWidth;
  90     final int    biHeight;
  91     final short  biPlanes;
  92     final short  biBitCount;
  93     final int    biCompression;
  94     final int    biSizeImage;
  95     final int    biXPelsPerMeter;
  96     final int    biYPelsPerMeter;
  97     final int    biClrUsed;
  98     final int    biClrImportant;
  99 
 100     BitmapInfoHeader(LEInputStream data) throws IOException {
 101         biSize = data.readInt();
 102         biWidth = data.readInt();
 103         biHeight = data.readInt();
 104         biPlanes = data.readShort();
 105         biBitCount = data.readShort();
 106         biCompression = data.readInt();
 107         biSizeImage = data.readInt();
 108         biXPelsPerMeter = data.readInt();
 109         biYPelsPerMeter = data.readInt();
 110         biClrUsed = data.readInt();
 111         biClrImportant = data.readInt();
 112 
 113         if (biSize > BIH_SIZE) {
 114             if (biSize == BIH4_SIZE || biSize == BIH5_SIZE) {
 115                 data.skipBytes(biSize - BIH_SIZE);
 116             } else {
 117                 throw new IOException("BitmapInfoHeader is corrupt");
 118             }
 119         }
 120         validate();
 121     }
 122 
 123     void validate() {
 124         if (biCompression != 0 || biPlanes != 1 || biBitCount != 24) {
 125             throw new RuntimeException(
 126                     "Unsupported BMP image: " +
 127                     "only 24 bit uncompressed BMP`s is supported");

















 128         }
 129     }
 130 }
 131 
 132 final class BMPImageLoader extends ImageLoaderImpl {
 133 
 134     static final short BM = 0x4D42;
 135     static final int BFH_SIZE = 14;
 136 
 137     final LEInputStream data;
 138 
 139     short bfType; // must be equal to BM
 140     int   bfSize;
 141     int   bfOffBits;
 142     int   bgra_palette[];


 143     BitmapInfoHeader bih;
 144 
 145     BMPImageLoader(InputStream input) throws IOException {
 146         super(BMPDescriptor.theInstance);
 147         data = new LEInputStream(input);
 148         bfType = data.readShort();
 149         if (isValid()) {
 150             readHeader();
 151         }

 152     }
 153 
 154     private void readHeader() throws IOException {
 155         bfSize = data.readInt();
 156         data.skipBytes(4); // 32  bits reserved
 157         bfOffBits = data.readInt();
 158         bih = new BitmapInfoHeader(data);






















 159         if (bih.biSize + BFH_SIZE != bfOffBits) {
 160             data.skipBytes(bfOffBits - bih.biSize - BFH_SIZE);
































































































































 161         }






































 162     }









 163 
 164     private boolean isValid() {
 165         return bfType == BM;





 166     }
 167 
 168     public void dispose() { }




 169 
 170     static void GBRtoRGB(byte data[], int pos, int size) {
 171         for (int sz = size / 3; sz != 0; --sz) {
 172             byte x = data[pos], y = data[pos + 2];
 173             data[pos + 2] = x; data[pos] = y;
 174             pos += 3;
 175         }
 176     }
 177 
 178     public ImageFrame load(int imageIndex, int width, int height,
 179             boolean preserveAspectRatio, boolean smooth) throws IOException
 180 {
 181         if (0 != imageIndex) {
 182             return null;
 183         }
 184 


 185         if ((width > 0 && width != bih.biWidth) ||
 186             (height > 0 && height != bih.biHeight))
 187         {
 188             throw new RuntimeException("scaling for BMP is not supported");
 189         }
 190 
 191         // Pass image metadata to any listeners.
 192         ImageMetadata imageMetadata = new ImageMetadata(null, Boolean.TRUE,
 193             null, null, null, null, bih.biWidth, bih.biHeight,
 194             null, null, null);
 195         updateImageMetadata(imageMetadata);
 196 
 197         int bmpStride = (bih.biBitCount*bih.biWidth/8 + 3) & ~3;
 198         int rowLength = (bih.biBitCount/8)*bih.biWidth;
 199 
 200         int hght = Math.abs(bih.biHeight);
 201 
 202         byte image[] = new byte[rowLength * hght];
 203 
 204         for (int i = 0; i != hght; ++i) {
 205             int line = bih.biHeight < 0 ? i : hght-i-1;
 206             int nRead = data.in.read(image, line * rowLength, rowLength);
 207             GBRtoRGB(image, line * rowLength, nRead);
 208 
 209             if (nRead != rowLength) {


 210                 break;





 211             }
 212 
 213             if (nRead < bmpStride) {
 214                 data.skipBytes(bmpStride-nRead);



 215             }










 216         }
 217 
 218         return new ImageFrame(ImageStorage.ImageType.RGB, ByteBuffer.wrap(image),
 219                 bih.biWidth, hght, rowLength, null, null);
 220     }
 221 }
 222 
 223 public final class BMPImageLoaderFactory implements ImageLoaderFactory {
 224 
 225     private static final BMPImageLoaderFactory theInstance =
 226             new BMPImageLoaderFactory();
 227 
 228     public static ImageLoaderFactory getInstance() {
 229         return theInstance;
 230     }
 231 
 232     public ImageFormatDescription getFormatDescription() {
 233         return BMPDescriptor.theInstance;
 234     }
 235 
 236     public ImageLoader createImageLoader(InputStream input) throws IOException {
 237         return new BMPImageLoader(input);
 238     }
 239 }
 240 


  67         int ch4 = in.read();
  68         if ((ch1 | ch2 | ch3 | ch4) < 0) {
  69             throw new EOFException();
  70         }
  71         return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
  72     }
  73 
  74     public final void skipBytes(int n) throws IOException {
  75         int m = (int)in.skip(n);
  76         if (m < n) {
  77             throw new EOFException();
  78         }
  79     }
  80 }
  81 
  82 final class BitmapInfoHeader {
  83 
  84     static final int BIH_SIZE = 40;
  85     static final int BIH4_SIZE = 108;
  86     static final int BIH5_SIZE = 124;
  87     static final int BI_RGB = 0;
  88     static final int BI_RLE8 = 1;
  89     static final int BI_RLE4 = 2;
  90     static final int BI_BITFIELDS = 3;
  91     static final int BI_JPEG = 4;
  92     static final int BI_PNG = 5;
  93 
  94     final int    biSize;
  95     final int    biWidth;
  96     final int    biHeight;
  97     final short  biPlanes;
  98     final short  biBitCount;
  99     final int    biCompression;
 100     final int    biSizeImage;
 101     final int    biXPelsPerMeter;
 102     final int    biYPelsPerMeter;
 103     final int    biClrUsed;
 104     final int    biClrImportant;
 105 
 106     BitmapInfoHeader(LEInputStream data) throws IOException {
 107         biSize = data.readInt();
 108         biWidth = data.readInt();
 109         biHeight = data.readInt();
 110         biPlanes = data.readShort();
 111         biBitCount = data.readShort();
 112         biCompression = data.readInt();
 113         biSizeImage = data.readInt();
 114         biXPelsPerMeter = data.readInt();
 115         biYPelsPerMeter = data.readInt();
 116         biClrUsed = data.readInt();
 117         biClrImportant = data.readInt();
 118 
 119         if (biSize > BIH_SIZE) {
 120             if (biSize == BIH4_SIZE || biSize == BIH5_SIZE) {
 121                 data.skipBytes(biSize - BIH_SIZE);
 122             } else {
 123                 throw new IOException("BitmapInfoHeader is corrupt");
 124             }
 125         }
 126         validate();
 127     }
 128 
 129     void validate() throws IOException {
 130         if (biBitCount < 1
 131                 || biCompression == BI_JPEG || biCompression == BI_PNG) {
 132             throw new IOException(
 133                     "Unsupported BMP image: "
 134                     + "Embedded JPEG or PNG images are not supported");
 135         }
 136 
 137         if (biCompression == BI_RLE4 && biBitCount != 4) {
 138             throw new IOException(
 139                     "Invalid BMP image: "
 140                     + "Only 4 bpp images can be RLE4 compressed");
 141         }
 142         if (biCompression == BI_RLE8 && biBitCount != 8) {
 143             throw new IOException(
 144                     "Invalid BMP image: "
 145                     + "Only 8 bpp images can be RLE8 compressed");
 146         }
 147         if (biCompression == BI_BITFIELDS) {
 148             throw new IOException(
 149                     "Unsupported BMP image: "
 150                     + "Bitfields BMP files are not supported");
 151         }
 152     }
 153 }
 154 
 155 final class BMPImageLoader extends ImageLoaderImpl {
 156 
 157     static final short BM = 0x4D42;
 158     static final int BFH_SIZE = 14;
 159 
 160     final LEInputStream data;
 161 

 162     int   bfSize;
 163     int   bfOffBits;
 164     byte  bgra_palette[];
 165     int   masks[] = new int[3];
 166     int   maskOffsets[] = new int[3];
 167     BitmapInfoHeader bih;
 168 
 169     BMPImageLoader(InputStream input) throws IOException {
 170         super(BMPDescriptor.theInstance);
 171         data = new LEInputStream(input);
 172         if (data.readShort() != BM) {
 173             throw new IOException("Invalid BMP file signature");

 174         }
 175         readHeader();
 176     }
 177 
 178     private void readHeader() throws IOException {
 179         bfSize = data.readInt();
 180         data.skipBytes(4); // 32  bits reserved
 181         bfOffBits = data.readInt();
 182         bih = new BitmapInfoHeader(data);
 183         if (bfOffBits < bih.biSize + BFH_SIZE) {
 184             throw new IOException("Invalid bitmap bits offset");
 185         }
 186 
 187         // assign default masks
 188         // TODO: parse BI_BITFIELDS
 189         if (bih.biBitCount == 16) {
 190             masks[0] = 0x7C00;
 191             masks[1] = 0x03E0;
 192             masks[2] = 0x001F;
 193             maskOffsets[0] = 10;
 194             maskOffsets[1] = 5;
 195             maskOffsets[2] = 0;
 196         } else if (bih.biBitCount == 32) {
 197             masks[0] = 0x00FF0000;
 198             masks[1] = 0x0000FF00;
 199             masks[2] = 0x000000FF;
 200             maskOffsets[0] = 24;
 201             maskOffsets[1] = 16;
 202             maskOffsets[2] = 0;
 203         }
 204 
 205         if (bih.biSize + BFH_SIZE != bfOffBits) {
 206             int length = bfOffBits - bih.biSize - BFH_SIZE;
 207             int paletteSize = length / 4;
 208             bgra_palette = new byte[paletteSize * 4];
 209             int read = data.in.read(bgra_palette);
 210             // goto bitmap bits
 211             if (read < length) {
 212                 data.in.skip(length - read);
 213             }
 214         }
 215     }
 216 
 217     @Override
 218     public void dispose() {
 219     }
 220 
 221     
 222     private void readRLE(byte[] image, int rowLength, int hght, boolean isRLE4)
 223             throws IOException
 224     {
 225         int imgSize = bih.biSizeImage;
 226         if (imgSize == 0) {
 227             imgSize = bfSize - bfOffBits;
 228         }
 229         byte imgData[] = new byte[imgSize];
 230         if (data.in.read(imgData) < imgSize) {
 231             return;
 232         }
 233 
 234         boolean isBottomUp = bih.biHeight > 0;
 235         int line = isBottomUp ? hght - 1 : 0;
 236         int i = 0;
 237         int x = 0;
 238         while (i < imgSize) {
 239             int b1 = getByte(imgData, i++);
 240             int b2 = getByte(imgData, i++);
 241             if (b1 == 0) { // absolute
 242                 switch (b2) {
 243                     case 0: // end of line
 244                         x = 0;
 245                         line += isBottomUp ? -1 : 1;
 246                         break;
 247                     case 1: // end of bitmap
 248                         return;
 249                     case 2: // delta
 250                         int deltaX = getByte(imgData, i++);
 251                         int deltaY = getByte(imgData, i++);
 252                         x += deltaX;
 253                         line += deltaY;
 254                         break;
 255                     default:
 256                         int indexData = 0;
 257                         int index;
 258                         for (int p = 0; p < b2; p++) {
 259                             if (isRLE4) {
 260                                 if ((p & 1) == 0) {
 261                                     indexData = getByte(imgData, i++);
 262                                     index = (indexData & 0xf0) >> 4;
 263                                 } else {
 264                                     index = indexData & 0x0f;
 265                                 }
 266                             } else {
 267                                 index = getByte(imgData, i++);
 268                             }
 269                             setRGBFromPalette(image, rowLength, x++, line, index);
 270                         }
 271                         if (isRLE4) {
 272                             if ((b2 & 3) == 1 || (b2 & 3) == 2) i++;
 273                         } else {
 274                             if ((b2 & 1) == 1) i++;
 275                         }
 276                         break;
 277                 }
 278             } else { // encoded
 279                 if (isRLE4) {
 280                     int index1 = (b2 & 0xf0) >> 4;
 281                     int index2 = b2 & 0x0f;
 282                     for (int p = 0; p < b1; p++) {
 283                         setRGBFromPalette(image, rowLength, x++, line,
 284                                 (p & 1) == 0 ? index1 : index2);
 285                     }
 286                 } else {
 287                     for (int p = 0; p < b1; p++) {
 288                         setRGBFromPalette(image, rowLength, x++, line, b2);
 289                     }
 290                 }
 291             }
 292         }
 293 
 294     }
 295 
 296     private void setRGBFromPalette(byte[] image, int rowLength, int x, int y, int index) {
 297         int i = y * rowLength + x * 3;
 298         image[i]     = bgra_palette[index * 4 + 2];
 299         image[i + 1] = bgra_palette[index * 4 + 1];
 300         image[i + 2] = bgra_palette[index * 4];
 301     }
 302 
 303     private void readPackedBits(byte[] image, int rowLength, int hght)
 304             throws IOException
 305     {
 306         int pixPerByte = 8 / bih.biBitCount;
 307         int bytesPerLine = (bih.biWidth + pixPerByte - 1) / pixPerByte;
 308         int srcStride = (bytesPerLine + 3) & ~3;
 309         int bitMask = (1 << bih.biBitCount) - 1;
 310 
 311         byte lineBuf[] = new byte[srcStride];
 312         for (int i = 0; i != hght; ++i) {
 313             int line = bih.biHeight < 0 ? i : hght - i - 1;
 314             int nRead = data.in.read(lineBuf);
 315 
 316             for (int x = 0; x != bih.biWidth; x++) {
 317                 int bitnum = x * bih.biBitCount;
 318                 int element = lineBuf[bitnum / 8];
 319                 int shift = 8 - (bitnum & 7) - bih.biBitCount;
 320                 int index = (element >> shift) & bitMask;
 321                 setRGBFromPalette(image, rowLength, x, line, index);
 322             }
 323             if (nRead != srcStride) {
 324                 break;
 325             }
 326         }
 327     }
 328 
 329     private static int getWord(byte[] buf, int pos) {
 330         return buf[pos] & 0xff | buf[pos + 1] << 8 & 0xff00;
 331     }
 332 
 333     private static int getByte(byte[] buf, int pos) {
 334         return buf[pos] & 0xff;
 335     }
 336 
 337     private void read16Bit(byte[] image, int rowLength, int hght) throws IOException {
 338         int bytesPerLine = bih.biWidth * 2;
 339         int srcStride = (bytesPerLine + 3) & ~3;
 340         byte lineBuf[] = new byte[srcStride];
 341         for (int i = 0; i != hght; ++i) {
 342             int line = bih.biHeight < 0 ? i : hght - i - 1;
 343             int nRead = data.in.read(lineBuf);
 344 
 345             for (int x = 0; x != bih.biWidth; x++) {
 346                 int element = getWord(lineBuf, x * 2);
 347                 for (int b = 0; b < 3; b++) {
 348                     byte c = (byte) ((element & masks[b]) >>> maskOffsets[b]);
 349                     c = (byte) ((double) c / ((1 << 5) - 1) * 255 + 0.5);
 350                     image[line * rowLength + x * 3 + b] = c;
 351                 }
 352             }
 353             if (nRead != srcStride) {
 354                 break;
 355             }
 356         }
 357     }
 358 
 359     private void read32Bit(byte[] image, int rowLength, int hght) throws IOException {
 360         int bytesPerLine = bih.biWidth * 4;
 361         byte lineBuf[] = new byte[bytesPerLine];
 362         for (int i = 0; i != hght; ++i) {
 363             int line = bih.biHeight < 0 ? i : hght - i - 1;
 364             int nRead = data.in.read(lineBuf);
 365 
 366             for (int x = 0; x != bih.biWidth; x++) {
 367                 int element = lineBuf[x] << 24
 368                         | lineBuf[x + 1] << 16
 369                         | lineBuf[x + 2] << 8
 370                         | lineBuf[x + 3];
 371                 for (int b = 0; b < 3; b++) {
 372                     image[line * rowLength + x * 3 + b]
 373                             = (byte) ((element & masks[b]) >>> maskOffsets[b]);
 374                 }
 375             }
 376             if (nRead != bytesPerLine) {
 377                 break;
 378             }
 379         }
 380     }
 381 
 382     private void read24Bit(byte[] image, int rowLength, int hght) throws IOException {
 383         int bmpStride = (rowLength + 3) & ~3;
 384 
 385         for (int i = 0; i != hght; ++i) {
 386             int line = bih.biHeight < 0 ? i : hght - i - 1;
 387             int nRead = data.in.read(image, line * rowLength, rowLength);
 388             GBRtoRGB(image, line * rowLength, nRead);
 389 
 390             if (nRead != rowLength) {
 391                 break;
 392             }
 393 
 394             if (nRead < bmpStride) {
 395                 data.skipBytes(bmpStride - nRead);
 396             }
 397         }
 398     }
 399 
 400     static void GBRtoRGB(byte data[], int pos, int size) {
 401         for (int sz = size / 3; sz != 0; --sz) {
 402             byte x = data[pos], y = data[pos + 2];
 403             data[pos + 2] = x; data[pos] = y;
 404             pos += 3;
 405         }
 406     }
 407 
 408     public ImageFrame load(int imageIndex, int width, int height,
 409             boolean preserveAspectRatio, boolean smooth) throws IOException
 410     {
 411         if (0 != imageIndex) {
 412             return null;
 413         }
 414 
 415         int hght = Math.abs(bih.biHeight);
 416 
 417         if ((width > 0 && width != bih.biWidth) ||
 418             (height > 0 && height != hght))
 419         {
 420             throw new IOException("scaling for BMP is not supported");
 421         }
 422 
 423         // Pass image metadata to any listeners.
 424         ImageMetadata imageMetadata = new ImageMetadata(null, Boolean.TRUE,
 425             null, null, null, null, bih.biWidth, hght,
 426             null, null, null);
 427         updateImageMetadata(imageMetadata);
 428 
 429         int stride = bih.biWidth * 3;





 430 
 431         byte image[] = new byte[stride * hght];



 432 
 433         switch (bih.biBitCount) {
 434             case 1:
 435                 readPackedBits(image, stride, hght);
 436                 break;
 437             case 4:
 438                 if (bih.biCompression == BitmapInfoHeader.BI_RLE4) {
 439                     readRLE(image, stride, hght, true);
 440                 } else {
 441                     readPackedBits(image, stride, hght);
 442                 }
 443                 break;
 444             case 8:
 445                 if (bih.biCompression == BitmapInfoHeader.BI_RLE8) {
 446                     readRLE(image, stride, hght, false);
 447                 } else {
 448                     readPackedBits(image, stride, hght);
 449                 }
 450                 break;
 451             case 16:
 452                 read16Bit(image, stride, hght);
 453                 break;
 454             case 32:
 455                 read32Bit(image, stride, hght);
 456                 break;
 457             case 24:
 458                 read24Bit(image, stride, hght);
 459                 break;
 460         }
 461 
 462         return new ImageFrame(ImageStorage.ImageType.RGB, ByteBuffer.wrap(image),
 463                 bih.biWidth, hght, stride, null, null);
 464     }
 465 }
 466 
 467 public final class BMPImageLoaderFactory implements ImageLoaderFactory {
 468 
 469     private static final BMPImageLoaderFactory theInstance =
 470             new BMPImageLoaderFactory();
 471 
 472     public static ImageLoaderFactory getInstance() {
 473         return theInstance;
 474     }
 475 
 476     public ImageFormatDescription getFormatDescription() {
 477         return BMPDescriptor.theInstance;
 478     }
 479 
 480     public ImageLoader createImageLoader(InputStream input) throws IOException {
 481         return new BMPImageLoader(input);
 482     }
 483 }