Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/352.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/maven/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用java.awt.image.buffereImage创建BIFF8位图记录需要很多时间-有没有更好的方法?_Java_Apache Poi_Awt_Bufferedimage - Fatal编程技术网

使用java.awt.image.buffereImage创建BIFF8位图记录需要很多时间-有没有更好的方法?

使用java.awt.image.buffereImage创建BIFF8位图记录需要很多时间-有没有更好的方法?,java,apache-poi,awt,bufferedimage,Java,Apache Poi,Awt,Bufferedimage,因此,我正在创建一个HSSFSheet,使用apache poi设置背景位图,并拥有自己的低级代码。记录位图的声明,BIFF8: 像素数据(位图的高度线阵列,从底线到顶线,见下文) 在每一行中,所有像素都从左向右写入。每个像素存储为3字节数组:像素颜色的红色、绿色和蓝色分量,按此顺序排列。通过在最后一个像素后插入零字节,将每行的大小对齐为4的倍数 完整声明见PDF图片: 为了实现这一点,我的方法是使用java.awt.image.BufferedImage类型BufferedImage.ty

因此,我正在创建一个
HSSFSheet
,使用
apache poi
设置背景位图,并拥有自己的低级代码。
记录位图的声明,BIFF8

像素数据(位图的高度线阵列,从底线到顶线,见下文)

在每一行中,所有像素都从左向右写入。每个像素存储为3字节数组:像素颜色的红色、绿色和蓝色分量,按此顺序排列。通过在最后一个像素后插入零字节,将每行的大小对齐为4的倍数

完整声明见PDF图片:

为了实现这一点,我的方法是使用
java.awt.image.BufferedImage
类型
BufferedImage.type\u3byte\ubgr
。然后从BuffereImage的光栅中以正确的顺序(从底行到顶行)获取所有字节R G B,并在宽度(x方向)上填充到4的倍数

见代码:

import java.io.FileOutputStream;
import java.io.FileInputStream;

import org.apache.poi.hssf.usermodel.*;

import org.apache.poi.hssf.record.RecordBase;
import org.apache.poi.hssf.record.StandardRecord;
import org.apache.poi.hssf.model.InternalSheet;
import org.apache.poi.util.LittleEndianOutput;

import java.lang.reflect.Field;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

import java.awt.image.BufferedImage;
import java.awt.Graphics2D;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.imageio.ImageIO;

public class CreateExcelHSSFSheetBackgroundBitmap {

 static List<Byte> getBackgroundBitmapData(String filePath) throws Exception {

  //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP

  List<Byte> data = new ArrayList<Byte>();

  // get file byte data in type BufferedImage.TYPE_3BYTE_BGR
  BufferedImage in = ImageIO.read(new FileInputStream(filePath));
  BufferedImage image = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
  Graphics2D graphics = image.createGraphics();
  graphics.drawImage(in, null, 0, 0);
  graphics.dispose();

  short width = (short)image.getWidth();
  short height = (short)image.getHeight();

  // each pixel has 3 bytes but the width bytes must be filled up to multiple of 4
  int widthBytesMultOf4 = (int)((width * 3 + 3) / 4 * 4);

// --- this part takes much time but I have not found any better possibility

  // put the bytes R G B into the data; lines of the bitmap must be from bottom line to top line
  int bytes = 0;
  for (short y = (short)(height - 1); y >= 0; y--) {
   for (short x = 0; x < width; x++) {
    int r = image.getData().getSample(x, y, 2);
    data.add(Byte.valueOf((byte)r));
    bytes++;
    int g = image.getData().getSample(x, y, 1);
    data.add(Byte.valueOf((byte)g));
    bytes++;
    int b = image.getData().getSample(x, y, 0);
    data.add(Byte.valueOf((byte)b));
    bytes++;
   } 
   // fill up x with 0 bytes up to multiple of 4
   for (int x = width * 3; x < widthBytesMultOf4; x++) {
    data.add(Byte.valueOf((byte)0));
    bytes++;
   }
  }

// ---

  // size  12 bytes (additional headers, see below) + picture bytes
  int size = 12 + bytes;

  // get size int as LITTLE_ENDIAN bytes
  ByteBuffer bSize = ByteBuffer.allocate(4);
  bSize.order(ByteOrder.LITTLE_ENDIAN);
  bSize.putInt(size);

  // get width short as LITTLE_ENDIAN bytes
  ByteBuffer bWidth = ByteBuffer.allocate(2);
  bWidth.order(ByteOrder.LITTLE_ENDIAN);
  bWidth.putShort(width);

  // get height short as LITTLE_ENDIAN bytes
  ByteBuffer bHeight = ByteBuffer.allocate(2);
  bHeight.order(ByteOrder.LITTLE_ENDIAN);
  bHeight.putShort(height);

  // put the record headers into the data
  Byte[] dataPart = new Byte[] { 0x09, 0x00, 0x01, 0x00, 
     bSize.array()[0], bSize.array()[1], bSize.array()[2], bSize.array()[3], // size
     //now 12 bytes follow
     0x0C, 0x00, 0x00, 0x00, 
     bWidth.array()[0], bWidth.array()[1], // width
     bHeight.array()[0], bHeight.array()[1], // height
     0x01, 0x00, 0x18, 0x00
   }; 

  data.addAll(0, Arrays.asList(dataPart));

  return data;
 }

 public static void main(String[] args) throws Exception {

  HSSFWorkbook workbook = new HSSFWorkbook();
  HSSFSheet sheet = workbook.createSheet("Sheet1");
  sheet = workbook.createSheet("Sheet2"); // this sheet gets the background image set

  // we need the binary records of the sheet
  // get InternalSheet
  Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
  _sheet.setAccessible(true); 
  InternalSheet internalsheet = (InternalSheet)_sheet.get(sheet); 

  // get List of RecordBase
  Field _records = InternalSheet.class.getDeclaredField("_records");
  _records.setAccessible(true);
  @SuppressWarnings("unchecked") 
  List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);

  // get bytes of the image file
  List<Byte> data = getBackgroundBitmapData("dummyText.png"); //PNG must not have transparency

  // do creating BitmapRecord and ContinueRecords from the data in parts of 8220 bytes
  BitmapRecord bitmapRecord = null;
  List<ContinueRecord> continueRecords = new ArrayList<ContinueRecord>();
  int bytes = 0;
  if (data.size() > 8220) {
   bitmapRecord = new BitmapRecord(data.subList(0, 8220));
   bytes = 8220;
   while (bytes < data.size()) {
    if ((bytes + 8220) < data.size()) {
     continueRecords.add(new ContinueRecord(data.subList(bytes, bytes + 8220)));
     bytes += 8220;
    } else {
     continueRecords.add(new ContinueRecord(data.subList(bytes, data.size())));
     break;
    }
   }
  } else {
   bitmapRecord = new BitmapRecord(data);
  }

  // add the records after PageSettingsBlock
  int i = 0;
  for (RecordBase r : records) {
   if (r instanceof org.apache.poi.hssf.record.aggregates.PageSettingsBlock) {
    break;
   }
   i++;
  }
  records.add(++i, bitmapRecord);
  for (ContinueRecord continueRecord : continueRecords) {
   records.add(++i, continueRecord);  
  }

  // debug output
  for (RecordBase r : internalsheet.getRecords()) {
   System.out.println(r);
  }

  // write out workbook
  workbook.write(new FileOutputStream("CreateExcelHSSFSheetBackgroundBitmap.xls"));
  workbook.close();

 }

 static class BitmapRecord extends StandardRecord {

  //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP

  List<Byte> data = new ArrayList<Byte>();

  BitmapRecord(List<Byte> data) {
   this.data = data;
  }

  public int getDataSize() { 
   return data.size(); 
  }

  public short getSid() {
   return (short)0x00E9;
  }

  public void serialize(LittleEndianOutput out) {
   for (Byte b : data) {
    out.writeByte(b);
   }
  }
 }

 static class ContinueRecord extends StandardRecord {

  //see https://www.openoffice.org/sc/excelfileformat.pdf - CONTINUE

  List<Byte> data = new ArrayList<Byte>();

  ContinueRecord(List<Byte> data) {
   this.data = data;
  }

  public int getDataSize() { 
   return data.size(); 
  }

  public short getSid() {
   return (short)0x003C;
  }

  public void serialize(LittleEndianOutput out) {
   for (Byte b : data) {
    out.writeByte(b);
   }
  }
 }

}

需要花费很多时间,因为根据上述奇怪的格式,需要获得每个像素的3字节rgb


有人知道更好的方法吗?也许上面这种奇怪的格式并不像我想象的那么奇怪,它已经有了其他的用途了?

通常只是程序员自己的愚蠢;-)。在德语中有一句谚语:“因为有树,所以看不见森林。”

只需将
buffereImage的
光栅
从循环外部获取一次,而不是一次又一次地从循环内部获取,即可极大地提高速度:

...
  // put the bytes R G B into the data; lines of the bitmap must be from bottom line to top line
  int bytes = 0;
  Raster raster = image.getData();
  for (short y = (short)(height - 1); y >= 0; y--) {
   for (short x = 0; x < width; x++) {
    int r = raster.getSample(x, y, 2);
    data.add(Byte.valueOf((byte)r));
    bytes++;
    int g = raster.getSample(x, y, 1);
    data.add(Byte.valueOf((byte)g));
    bytes++;
    int b = raster.getSample(x, y, 0);
    data.add(Byte.valueOf((byte)b));
    bytes++;
   } 
   // fill up x with 0 bytes up to multiple of 4
   for (int x = width * 3; x < widthBytesMultOf4; x++) {
    data.add(Byte.valueOf((byte)0));
    bytes++;
   }
  }
...

这是你的代码的一个修改版本,对我来说很有效,而且速度非常快

  • 我到处使用
    byte[]
    (和
    ByteArrayOutputStream
    ),没有更多的
    列表
  • 由于我们已经有了
    类型为3BYTE\u BGR
    缓冲图像
    ,我们几乎可以直接将其用作BMP输出。我们只需要a)预先设置一个有效的BMP头,b)自下而上写入,c)将每个扫描线(行)填充到32位边界,d)切换BGR->RGB顺序
  • 我使用
    光栅
    将数据行复制(填充)到输出中,因为复制较大的数据块比复制单个字节快
  • 如注释中所述,该结构是标准BMP,带有
    BITMAPCOREHEADER
    (且无文件头)。不幸的是,
    ImageIO
    BMPImageWriter
    总是写入文件头并使用40字节的
    BitMapInfo头。您可能可以绕过这些问题,使用标准的书写器,只需稍微调整一下数据(提示:文件头包含偏移量为10的像素数据偏移量),但由于核心BMP格式实现起来很简单,因此可能与下面的操作一样简单

    虽然文档中明确表示直接使用其他格式,如PNG和JPEG,但我没有做到这一点

    如果您愿意,可能还有改进的余地,以避免某些字节数组复制(即使用偏移量/长度,并将整个数据数组传递给
    位图/连续记录
    s,而不是
    数组。copyOfRange()

    代码:

    导入java.awt.Graphics2D;
    导入java.awt.image.buffereImage;
    导入java.awt.image.graster;
    导入java.io.ByteArrayOutputStream;
    导入java.io.FileInputStream;
    导入java.io.FileOutputStream;
    导入java.lang.reflect.Field;
    导入java.nio.ByteBuffer;
    导入java.nio.ByteOrder;
    导入java.util.ArrayList;
    导入java.util.array;
    导入java.util.List;
    导入javax.imageio.imageio;
    导入org.apache.poi.hssf.model.InternalSheet;
    导入org.apache.poi.hssf.record.RecordBase;
    导入org.apache.poi.hssf.record.StandardRecord;
    导入org.apache.poi.hssf.usermodel.HSSFSheet;
    导入org.apache.poi.hssf.usermodel.HSSFWorkbook;
    导入org.apache.poi.util.LittleEndianOutput;
    公共类CreateExcelHSSFSheetBackgroundBitmap{
    静态字节[]getBackgroundBitmapData(字符串文件路径)引发异常{
    //看https://www.openoffice.org/sc/excelfileformat.pdf -位图
    //获取BuffereImage.type_3BYTE_BGR类型中的文件字节数据
    BuffereImage in=ImageIO.read(新文件输入流(文件路径));
    BuffereImage=新的BuffereImage(in.getWidth()、in.getHeight()、BuffereImage.TYPE_3BYTE_BGR);
    Graphics2D graphics=image.createGraphics();
    graphics.drawImage(in,null,0,0);
    graphics.dispose();
    //计算行大小(c)
    int rowSize=((24*image.getWidth()+31)/32)*4;
    ByteArrayOutputStream输出=新建ByteArrayOutputStream(image.getHeight()*行大小*3+1024);
    //将记录头放入数据中
    ByteBuffer头=ByteBuffer.allocate(8+12);
    header.order(ByteOrder.LITTLE_ENDIAN);
    //无证XLS资料
    header.putShort((short)0x09);
    header.putShort((short)0x01);
    header.putInt(image.getHeight()*rowSize+12);//图像流的大小
    //BITMAPCOREHEADER(a)
    标题。putInt(12);
    header.putShort((short)image.getWidth());
    header.putShort((short)image.getHeight());//如果自顶向下写入,则使用-height
    header.putShort((short)1);//平面,始终为1
    header.putShort((short)24);//位计数
    output.write(header.array());
    //自下而上的输出行(b)
    光栅=image.getRaster()
    .createChild(0,0,image.getWidth(),image.getHeight(),0,0,新int[]{2,1,0});//反向BGR->RGB(d)
    byte[]行=新字节[rowSize];//填充(c)
    对于(int i=image.getHeight()-1;i>=0;i--){
    行=(字节[])raster.getDataElements(0,i,image.getWidth(),1,行);
    输出。写入(行);
    }
    返回output.toByteArray();
    }
    公共静态vo
    
    // ---
    
    ...
      // put the bytes R G B into the data; lines of the bitmap must be from bottom line to top line
      int bytes = 0;
      Raster raster = image.getData();
      for (short y = (short)(height - 1); y >= 0; y--) {
       for (short x = 0; x < width; x++) {
        int r = raster.getSample(x, y, 2);
        data.add(Byte.valueOf((byte)r));
        bytes++;
        int g = raster.getSample(x, y, 1);
        data.add(Byte.valueOf((byte)g));
        bytes++;
        int b = raster.getSample(x, y, 0);
        data.add(Byte.valueOf((byte)b));
        bytes++;
       } 
       // fill up x with 0 bytes up to multiple of 4
       for (int x = width * 3; x < widthBytesMultOf4; x++) {
        data.add(Byte.valueOf((byte)0));
        bytes++;
       }
      }
    ...
    
    import java.io.FileOutputStream;
    import java.io.FileInputStream;
    import java.io.ByteArrayOutputStream;
    
    import org.apache.poi.hssf.usermodel.*;
    
    import org.apache.poi.hssf.record.RecordBase;
    import org.apache.poi.hssf.record.StandardRecord;
    import org.apache.poi.hssf.record.ContinueRecord;
    import org.apache.poi.hssf.model.InternalSheet;
    
    import org.apache.poi.util.LittleEndianOutput;
    
    import java.lang.reflect.Field;
    
    import java.util.List;
    import java.util.ArrayList;
    import java.util.Arrays;
    
    import java.awt.image.BufferedImage;
    import java.awt.image.Raster;
    import java.awt.Graphics2D;
    
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    
    import javax.imageio.ImageIO;
    
    public class CreateExcelHSSFSheetBackgroundBMP {
    
     static byte[] getBackgroundBitmapData(String filePath) throws Exception {
    
      // see https://www.openoffice.org/sc/excelfileformat.pdf - 5.6 BITMAP
      // and https://interoperability.blob.core.windows.net/files/MS-XLS/[MS-XLS].pdf - 2.4.19 BkHim
    
      // get file byte data in type BufferedImage.TYPE_3BYTE_BGR
      BufferedImage in = ImageIO.read(new FileInputStream(filePath));
      BufferedImage image = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
      Graphics2D graphics = image.createGraphics();
      graphics.drawImage(in, null, 0, 0);
      graphics.dispose();
    
      short width = (short)image.getWidth();
      short height = (short)image.getHeight();
    
      // each pixel has 3 bytes but the width bytes must be filled up to multiple of 4
      int widthBytesMultOf4 = (int)((width * 3 + 3) / 4 * 4);
    
      // size 12 bytes (additional headers, see below) + picture bytes
      int size = 12 + height * widthBytesMultOf4;
    
      // create the header section
      ByteBuffer headers = ByteBuffer.allocate(20);
      headers.order(ByteOrder.LITTLE_ENDIAN);
      headers.putShort((short)0x09); // 0x0009 = signed integer that specifies the image format BMP
      headers.putShort((short)0x01); // reserved (2 bytes): MUST be 0x0001
      headers.putInt(size); // signed integer that specifies the size of imageBlob in bytes
      // BMP header section:
      headers.putInt(0x0C); // length 0x0C = 12 bytes
      headers.putShort(width); // pixels width
      headers.putShort(height); // pixels heigth
      headers.putShort((short)0x01); // number of planes: always 1
      headers.putShort((short)0x18); // color depth 0x018 = 24 bit
    
      //create data ByteArrayOutputStream
      ByteArrayOutputStream data = new ByteArrayOutputStream();
    
      // write headers section
      data.write(headers.array());
    
      // put the bytes R G B into the data; lines of the bitmap must be from bottom line to top line
      Raster raster = image.getData();
      for (short y = (short)(height - 1); y >= 0; y--) {
       for (short x = 0; x < width; x++) {
        int r = raster.getSample(x, y, 2);
        data.write((byte)r);
        int g = raster.getSample(x, y, 1);
        data.write((byte)g);
        int b = raster.getSample(x, y, 0);
        data.write((byte)b);
       } 
       // fill up x with 0 bytes up to multiple of 4
       for (int x = width * 3; x < widthBytesMultOf4; x++) {
        data.write((byte)0);
       }
      }
    
      return data.toByteArray();
     }
    
     public static void main(String[] args) throws Exception {
    
      HSSFWorkbook workbook = new HSSFWorkbook();
      HSSFSheet sheet = workbook.createSheet("Sheet1");
      sheet = workbook.createSheet("Sheet2"); // this sheet gets the background image set
    
      // we need the binary records of the sheet
      // get InternalSheet
      Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
      _sheet.setAccessible(true); 
      InternalSheet internalsheet = (InternalSheet)_sheet.get(sheet); 
    
      // get List of RecordBase
      Field _records = InternalSheet.class.getDeclaredField("_records");
      _records.setAccessible(true);
      @SuppressWarnings("unchecked") 
      List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
    
      // get bytes of the image file
      byte[] data = getBackgroundBitmapData("dummyText.png"); //PNG must not have transparency
    
      // do creating BitmapRecord and ContinueRecords from the data in parts of 8220 bytes
      BitmapRecord bitmapRecord = null;
      List<ContinueRecord> continueRecords = new ArrayList<ContinueRecord>();
      int bytes = 0;
      if (data.length > 8220) {
       bitmapRecord = new BitmapRecord(Arrays.copyOfRange(data, 0, 8220));
       bytes = 8220;
       while (bytes < data.length) {
        if ((bytes + 8220) < data.length) {
         continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, bytes + 8220)));
         bytes += 8220;
        } else {
         continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, data.length)));
         break;
        }
       }
      } else {
       bitmapRecord = new BitmapRecord(data);
      }
    
      // add the records after PageSettingsBlock
      int i = 0;
      for (RecordBase r : records) {
       if (r instanceof org.apache.poi.hssf.record.aggregates.PageSettingsBlock) {
        break;
       }
       i++;
      }
      records.add(++i, bitmapRecord);
      for (ContinueRecord continueRecord : continueRecords) {
       records.add(++i, continueRecord);  
      }
    
      // debug output
      for (RecordBase r : internalsheet.getRecords()) {
       System.out.println(r.getClass());
      }
    
      // write out workbook
      FileOutputStream out = new FileOutputStream("CreateExcelHSSFSheetBackgroundBMP.xls");
      workbook.write(out);
      workbook.close();
      out.close();
    
     }
    
     static class BitmapRecord extends StandardRecord {
    
      // see https://www.openoffice.org/sc/excelfileformat.pdf - 5.6 BITMAP
      // and https://interoperability.blob.core.windows.net/files/MS-XLS/[MS-XLS].pdf - 2.4.19 BkHim
    
      byte[] data;
    
      BitmapRecord(byte[] data) {
       this.data = data;
      }
    
      public int getDataSize() { 
       return data.length; 
      }
    
      public short getSid() {
       return (short)0x00E9;
      }
    
      public void serialize(LittleEndianOutput out) {
       out.write(data);
      }
     }
    
    }
    
    import java.awt.Graphics2D;
    import java.awt.image.BufferedImage;
    import java.awt.image.Raster;
    import java.io.ByteArrayOutputStream;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.lang.reflect.Field;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import javax.imageio.ImageIO;
    
    import org.apache.poi.hssf.model.InternalSheet;
    import org.apache.poi.hssf.record.RecordBase;
    import org.apache.poi.hssf.record.StandardRecord;
    import org.apache.poi.hssf.usermodel.HSSFSheet;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.util.LittleEndianOutput;
    
    public class CreateExcelHSSFSheetBackgroundBitmap {
    
        static byte[] getBackgroundBitmapData(String filePath) throws Exception {
    
            //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP
    
            // get file byte data in type BufferedImage.TYPE_3BYTE_BGR
            BufferedImage in = ImageIO.read(new FileInputStream(filePath));
            BufferedImage image = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
            Graphics2D graphics = image.createGraphics();
            graphics.drawImage(in, null, 0, 0);
            graphics.dispose();
    
            // calculate row size (c)
            int rowSize = ((24 * image.getWidth() + 31) / 32) * 4;
    
            ByteArrayOutputStream output = new ByteArrayOutputStream(image.getHeight() * rowSize * 3 + 1024);
    
            // put the record headers into the data
            ByteBuffer header = ByteBuffer.allocate(8 + 12);
            header.order(ByteOrder.LITTLE_ENDIAN);
    
            // Undocumented XLS stuff
            header.putShort((short) 0x09);
            header.putShort((short) 0x01);
            header.putInt(image.getHeight() * rowSize + 12); // Size of image stream
    
            // BITMAPCOREHEADER (a)
            header.putInt(12);
    
            header.putShort((short) image.getWidth());
            header.putShort((short) image.getHeight()); // Use -height if writing top-down
    
            header.putShort((short) 1); // planes, always 1
            header.putShort((short) 24); // bitcount
    
            output.write(header.array());
    
            // Output rows bottom-up (b)
            Raster raster = image.getRaster()
                                 .createChild(0, 0, image.getWidth(), image.getHeight(), 0, 0, new int[]{2, 1, 0}); // Reverse BGR -> RGB (d)
            byte[] row = new byte[rowSize]; // padded (c)
    
            for (int i = image.getHeight() - 1; i >= 0; i--) {
                row = (byte[]) raster.getDataElements(0, i, image.getWidth(), 1, row);
                output.write(row);
            }
    
            return output.toByteArray();
        }
    
        public static void main(String[] args) throws Exception {
            HSSFWorkbook workbook = new HSSFWorkbook();
            HSSFSheet sheet = workbook.createSheet("Sheet2"); // this sheet gets the background image set
    
            // we need the binary records of the sheet
            // get InternalSheet
            Field _sheet = HSSFSheet.class.getDeclaredField("_sheet");
            _sheet.setAccessible(true);
            InternalSheet internalsheet = (InternalSheet)_sheet.get(sheet);
    
            // get List of RecordBase
            Field _records = InternalSheet.class.getDeclaredField("_records");
            _records.setAccessible(true);
            @SuppressWarnings("unchecked")
            List<RecordBase> records = (List<RecordBase>)_records.get(internalsheet);
    
            // get bytes of the image file
            byte[] data = getBackgroundBitmapData("dummy.png"); //PNG must not have transparency
    
            // do creating BitmapRecord and ContinueRecords from the data in parts of 8220 bytes
            BitmapRecord bitmapRecord;
            List<ContinueRecord> continueRecords = new ArrayList<>();
            int bytes;
    
            if (data.length > 8220) {
                bitmapRecord = new BitmapRecord(Arrays.copyOfRange(data, 0, 8220));
                bytes = 8220;
                while (bytes < data.length) {
                    if ((bytes + 8220) < data.length) {
                        continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, bytes + 8220)));
                        bytes += 8220;
                    } else {
                        continueRecords.add(new ContinueRecord(Arrays.copyOfRange(data, bytes, data.length)));
                        break;
                    }
                }
            } else {
                bitmapRecord = new BitmapRecord(data);
            }
    
            // add the records after PageSettingsBlock
            int i = 0;
            for (RecordBase r : records) {
                if (r instanceof org.apache.poi.hssf.record.aggregates.PageSettingsBlock) {
                    break;
                }
                i++;
            }
            records.add(++i, bitmapRecord);
            for (ContinueRecord continueRecord : continueRecords) {
                records.add(++i, continueRecord);
            }
    
            // debug output
            for (RecordBase r : internalsheet.getRecords()) {
                System.out.println(r);
            }
    
            // write out workbook
            workbook.write(new FileOutputStream("backgroundImage.xls"));
            workbook.close();
    
        }
    
        static class BitmapRecord extends StandardRecord {
    
            //see https://www.openoffice.org/sc/excelfileformat.pdf - BITMAP
    
            byte[] data;
    
            BitmapRecord(byte[] data) {
                this.data = data;
            }
    
            public int getDataSize() {
                return data.length;
            }
    
            public short getSid() {
                return (short)0x00E9;
            }
    
            public void serialize(LittleEndianOutput out) {
                out.write(data);
            }
        }
    
        static class ContinueRecord extends StandardRecord {
    
            //see https://www.openoffice.org/sc/excelfileformat.pdf - CONTINUE
    
            byte[] data;
    
            ContinueRecord(byte[] data) {
                this.data = data;
            }
    
            public int getDataSize() {
                return data.length;
            }
    
            public short getSid() {
                return (short)0x003C;
            }
    
            public void serialize(LittleEndianOutput out) {
                out.write(data);
            }
        }
    }