| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| package org.apache.poi.hwmf.record; |
| |
| import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString; |
| import static org.apache.poi.hwmf.record.HwmfDraw.readPointS; |
| |
| import java.awt.Color; |
| import java.awt.Shape; |
| import java.awt.geom.Path2D; |
| import java.awt.geom.Point2D; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.image.BufferedImage; |
| import java.io.IOException; |
| |
| import org.apache.poi.hwmf.draw.HwmfDrawProperties; |
| import org.apache.poi.hwmf.draw.HwmfGraphics; |
| import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode; |
| import org.apache.poi.util.LittleEndianConsts; |
| import org.apache.poi.util.LittleEndianInputStream; |
| |
| public class HwmfFill { |
| /** |
| * A record which contains an image (to be extracted) |
| */ |
| public interface HwmfImageRecord { |
| |
| default BufferedImage getImage() { |
| return getImage(Color.BLACK, new Color(0x00FFFFFF, true), true); |
| } |
| |
| /** |
| * Provide an image using the fore-/background color, in case of a 1-bit pattern |
| * @param foreground the foreground color |
| * @param background the background color |
| * @param hasAlpha if true, the background color is rendered transparent - see {@link HwmfMisc.WmfSetBkMode.HwmfBkMode} |
| * @return the image |
| * |
| * @since POI 4.1.1 |
| */ |
| BufferedImage getImage(Color foreground, Color background, boolean hasAlpha); |
| |
| /** |
| * @return the raw BMP data |
| * |
| * @see <a href="https://en.wikipedia.org/wiki/BMP_file_format">BMP format</a> |
| * @since POI 4.1.1 |
| */ |
| byte[] getBMPData(); |
| } |
| |
| /** |
| * The ColorUsage Enumeration (a 16-bit unsigned integer) specifies whether a color table |
| * exists in a device-independent bitmap (DIB) and how to interpret its values, |
| * i.e. if contains explicit RGB values or indexes into a palette. |
| */ |
| public enum ColorUsage { |
| /** |
| * The color table contains RGB values |
| */ |
| DIB_RGB_COLORS(0x0000), |
| /** |
| * The color table contains 16-bit indices into the current logical palette in |
| * the playback device context. |
| */ |
| DIB_PAL_COLORS(0x0001), |
| /** |
| * No color table exists. The pixels in the DIB are indices into the current |
| * logical palette in the playback device context. |
| */ |
| DIB_PAL_INDICES(0x0002) |
| ; |
| |
| |
| public final int flag; |
| ColorUsage(int flag) { |
| this.flag = flag; |
| } |
| |
| public static ColorUsage valueOf(int flag) { |
| for (ColorUsage bs : values()) { |
| if (bs.flag == flag) return bs; |
| } |
| return null; |
| } |
| } |
| |
| |
| /** |
| * The META_FILLREGION record fills a region using a specified brush. |
| */ |
| public static class WmfFillRegion implements HwmfRecord { |
| |
| /** |
| * A 16-bit unsigned integer used to index into the WMF Object Table to get |
| * the region to be filled. |
| */ |
| protected int regionIndex; |
| |
| /** |
| * A 16-bit unsigned integer used to index into the WMF Object Table to get the |
| * brush to use for filling the region. |
| */ |
| protected int brushIndex; |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.fillRegion; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| regionIndex = leis.readUShort(); |
| brushIndex = leis.readUShort(); |
| return 2*LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| ctx.applyObjectTableEntry(regionIndex); |
| ctx.applyObjectTableEntry(brushIndex); |
| |
| Shape region = ctx.getProperties().getRegion(); |
| if (region != null) { |
| ctx.fill(region); |
| } |
| |
| } |
| } |
| |
| /** |
| * The META_PAINTREGION record paints the specified region by using the brush that is |
| * defined in the playback device context. |
| */ |
| public static class WmfPaintRegion implements HwmfRecord { |
| |
| /** |
| * A 16-bit unsigned integer used to index into the WMF Object Table to get |
| * the region to be painted. |
| */ |
| int regionIndex; |
| |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.paintRegion; |
| } |
| |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| regionIndex = leis.readUShort(); |
| return LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| ctx.applyObjectTableEntry(regionIndex); |
| |
| Shape region = ctx.getProperties().getRegion(); |
| if (region != null) { |
| ctx.fill(region); |
| } |
| } |
| } |
| |
| |
| /** |
| * The META_FLOODFILL record fills an area of the output surface with the brush that |
| * is defined in the playback device context. |
| */ |
| public static class WmfFloodFill implements HwmfRecord { |
| |
| /** |
| * A 32-bit ColorRef Object that defines the color value. |
| */ |
| protected final HwmfColorRef colorRef = new HwmfColorRef(); |
| |
| /** the point where filling is to start. */ |
| protected final Point2D start = new Point2D.Double(); |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.floodFill; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| int size = colorRef.init(leis); |
| size += readPointS(leis, start); |
| return size; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| |
| } |
| } |
| |
| /** |
| * The META_SETPOLYFILLMODE record sets polygon fill mode in the playback device context for |
| * graphics operations that fill polygons. |
| */ |
| public static class WmfSetPolyfillMode implements HwmfRecord { |
| /** |
| * A 16-bit unsigned integer that defines polygon fill mode. |
| * This MUST be one of the values: ALTERNATE = 0x0001, WINDING = 0x0002 |
| */ |
| public enum HwmfPolyfillMode { |
| /** |
| * Selects alternate mode (fills the area between odd-numbered and |
| * even-numbered polygon sides on each scan line). |
| */ |
| ALTERNATE(0x0001, Path2D.WIND_EVEN_ODD), |
| /** |
| * Selects winding mode (fills any region with a nonzero winding value). |
| */ |
| WINDING(0x0002, Path2D.WIND_NON_ZERO); |
| |
| public final int wmfFlag; |
| public final int awtFlag; |
| HwmfPolyfillMode(int wmfFlag, int awtFlag) { |
| this.wmfFlag = wmfFlag; |
| this.awtFlag = awtFlag; |
| } |
| |
| public static HwmfPolyfillMode valueOf(int wmfFlag) { |
| for (HwmfPolyfillMode pm : values()) { |
| if (pm.wmfFlag == wmfFlag) return pm; |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * An unsigned integer that defines polygon fill mode. |
| * This MUST be one of the values: ALTERNATE = 0x0001, WINDING = 0x0002 |
| */ |
| protected HwmfPolyfillMode polyFillMode; |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.setPolyFillMode; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| polyFillMode = HwmfPolyfillMode.valueOf(leis.readUShort() & 3); |
| return LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| ctx.getProperties().setPolyfillMode(polyFillMode); |
| } |
| |
| @Override |
| public String toString() { |
| return "{ polyFillMode: '"+ polyFillMode +"' }"; |
| } |
| } |
| |
| |
| /** |
| * The META_EXTFLOODFILL record fills an area with the brush that is defined in |
| * the playback device context. |
| */ |
| public static class WmfExtFloodFill extends WmfFloodFill { |
| |
| /** |
| * A 16-bit unsigned integer that defines the fill operation to be performed. This |
| * member MUST be one of the values in the FloodFill Enumeration table: |
| * |
| * FLOODFILLBORDER = 0x0000: |
| * The fill area is bounded by the color specified by the Color member. |
| * This style is identical to the filling performed by the META_FLOODFILL record. |
| * |
| * FLOODFILLSURFACE = 0x0001: |
| * The fill area is bounded by the color that is specified by the Color member. |
| * Filling continues outward in all directions as long as the color is encountered. |
| * This style is useful for filling areas with multicolored boundaries. |
| */ |
| protected int mode; |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.extFloodFill; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| mode = leis.readUShort(); |
| return super.init(leis, recordSize, recordFunction)+LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| |
| } |
| } |
| |
| /** |
| * The META_INVERTREGION record draws a region in which the colors are inverted. |
| */ |
| public static class WmfInvertRegion implements HwmfRecord { |
| |
| /** |
| * A 16-bit unsigned integer used to index into the WMF Object Table to get |
| * the region to be inverted. |
| */ |
| private int region; |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.invertRegion; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| region = leis.readUShort(); |
| return LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| |
| } |
| } |
| |
| |
| /** |
| * The META_PATBLT record paints a specified rectangle using the brush that is defined in the playback |
| * device context. The brush color and the surface color or colors are combined using the specified |
| * raster operation. |
| */ |
| public static class WmfPatBlt implements HwmfRecord { |
| |
| /** |
| * A 32-bit unsigned integer that defines the raster operation code. |
| * This code MUST be one of the values in the Ternary Raster Operation enumeration table. |
| */ |
| private HwmfTernaryRasterOp rasterOperation; |
| |
| private final Rectangle2D bounds = new Rectangle2D.Double(); |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.patBlt; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| int rasterOpCode = leis.readUShort(); |
| int rasterOpIndex = leis.readUShort(); |
| |
| rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); |
| assert(rasterOpCode == rasterOperation.opCode); |
| |
| return readBounds2(leis, bounds)+2*LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| |
| } |
| } |
| |
| /** |
| * The META_STRETCHBLT record specifies the transfer of a block of pixels according to a raster |
| * operation, with possible expansion or contraction. |
| * The destination of the transfer is the current output region in the playback device context. |
| * There are two forms of META_STRETCHBLT, one which specifies a bitmap as the source, and the other |
| * which uses the playback device context as the source. Definitions follow for the fields that are the |
| * same in the two forms of META_STRETCHBLT are defined below. The subsections that follow specify |
| * the packet structures of the two forms of META_STRETCHBLT. |
| * The expansion or contraction is performed according to the stretching mode currently set in the |
| * playback device context, which MUST be a value from the StretchMode. |
| */ |
| public static class WmfStretchBlt implements HwmfRecord { |
| /** |
| * A 32-bit unsigned integer that defines how the source pixels, the current brush |
| * in the playback device context, and the destination pixels are to be combined to form the new |
| * image. This code MUST be one of the values in the Ternary Raster Operation Enumeration |
| */ |
| protected HwmfTernaryRasterOp rasterOperation; |
| |
| /** the source rectangle */ |
| protected final Rectangle2D srcBounds = new Rectangle2D.Double(); |
| |
| /** the destination rectangle */ |
| protected final Rectangle2D dstBounds = new Rectangle2D.Double(); |
| |
| /** |
| * A variable-sized Bitmap16 Object that defines source image content. |
| * This object MUST be specified, even if the raster operation does not require a source. |
| */ |
| protected HwmfBitmap16 target; |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.stretchBlt; |
| } |
| |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3)); |
| |
| int rasterOpCode = leis.readUShort(); |
| int rasterOpIndex = leis.readUShort(); |
| |
| rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); |
| assert(rasterOpCode == rasterOperation.opCode); |
| |
| int size = 2*LittleEndianConsts.SHORT_SIZE; |
| |
| size += readBounds2(leis, srcBounds); |
| |
| if (!hasBitmap) { |
| /*int reserved =*/ leis.readShort(); |
| size += LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| size += readBounds2(leis, dstBounds); |
| |
| if (hasBitmap) { |
| target = new HwmfBitmap16(); |
| size += target.init(leis); |
| } |
| |
| return size; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| |
| } |
| |
| @Override |
| public String toString() { |
| return |
| "{ rasterOperation: '"+rasterOperation+"'"+ |
| ", srcBounds: "+boundsToString(srcBounds)+ |
| ", dstBounds: "+boundsToString(dstBounds)+ |
| "}"; |
| } |
| } |
| |
| /** |
| * The META_STRETCHDIB record specifies the transfer of color data from a |
| * block of pixels in device independent format according to a raster operation, |
| * with possible expansion or contraction. |
| * The source of the color data is a DIB, and the destination of the transfer is |
| * the current output region in the playback device context. |
| */ |
| public static class WmfStretchDib implements HwmfRecord, HwmfImageRecord { |
| /** |
| * A 32-bit unsigned integer that defines how the source pixels, the current brush in |
| * the playback device context, and the destination pixels are to be combined to |
| * form the new image. |
| */ |
| protected HwmfTernaryRasterOp rasterOperation; |
| |
| /** |
| * A 16-bit unsigned integer that defines whether the Colors field of the |
| * DIB contains explicit RGB values or indexes into a palette. |
| */ |
| protected ColorUsage colorUsage; |
| |
| /** the source rectangle. */ |
| protected final Rectangle2D srcBounds = new Rectangle2D.Double(); |
| |
| /** the destination rectangle. */ |
| protected final Rectangle2D dstBounds = new Rectangle2D.Double(); |
| |
| /** |
| * A variable-sized DeviceIndependentBitmap Object (section 2.2.2.9) that is the |
| * source of the color data. |
| */ |
| protected final HwmfBitmapDib bitmap = new HwmfBitmapDib(); |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.stretchDib; |
| } |
| |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| int rasterOpCode = leis.readUShort(); |
| int rasterOpIndex = leis.readUShort(); |
| |
| rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); |
| assert(rasterOpCode == rasterOperation.opCode); |
| |
| colorUsage = ColorUsage.valueOf(leis.readUShort()); |
| |
| int size = 3*LittleEndianConsts.SHORT_SIZE; |
| |
| size += readBounds2(leis, srcBounds); |
| size += readBounds2(leis, dstBounds); |
| |
| size += bitmap.init(leis, (int)(recordSize-6-size)); |
| |
| return size; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| HwmfDrawProperties prop = ctx.getProperties(); |
| prop.setRasterOp(rasterOperation); |
| if (bitmap.isValid()) { |
| BufferedImage bi = bitmap.getImage(prop.getPenColor().getColor(), prop.getBackgroundColor().getColor(), |
| prop.getBkMode() == HwmfBkMode.TRANSPARENT); |
| ctx.drawImage(bi, srcBounds, dstBounds); |
| } else if (!dstBounds.isEmpty()) { |
| BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); |
| ctx.drawImage(bi, new Rectangle2D.Double(0,0,100,100), dstBounds); |
| } |
| } |
| |
| @Override |
| public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) { |
| return bitmap.getImage(foreground,background,hasAlpha); |
| } |
| |
| public HwmfBitmapDib getBitmap() { |
| return bitmap; |
| } |
| |
| @Override |
| public byte[] getBMPData() { |
| return bitmap.getBMPData(); |
| } |
| |
| @Override |
| public String toString() { |
| return |
| "{ rasterOperation: '"+rasterOperation+"'"+ |
| ", colorUsage: '"+colorUsage+"'"+ |
| ", srcBounds: "+boundsToString(srcBounds)+ |
| ", dstBounds: "+boundsToString(dstBounds)+ |
| "}"; |
| } |
| } |
| |
| public static class WmfBitBlt extends WmfStretchBlt { |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.bitBlt; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3)); |
| |
| int rasterOpCode = leis.readUShort(); |
| int rasterOpIndex = leis.readUShort(); |
| |
| rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); |
| assert(rasterOpCode == rasterOperation.opCode); |
| |
| int size = 2*LittleEndianConsts.SHORT_SIZE; |
| |
| final Point2D srcPnt = new Point2D.Double(); |
| size += readPointS(leis, srcPnt); |
| |
| if (!hasBitmap) { |
| /*int reserved =*/ leis.readShort(); |
| size += LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| size += readBounds2(leis, dstBounds); |
| |
| if (hasBitmap) { |
| target = new HwmfBitmap16(); |
| size += target.init(leis); |
| } |
| |
| srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight()); |
| |
| return size; |
| } |
| } |
| |
| |
| /** |
| * The META_SETDIBTODEV record sets a block of pixels in the playback device context |
| * using deviceindependent color data. |
| * The source of the color data is a DIB |
| */ |
| public static class WmfSetDibToDev implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry { |
| |
| /** |
| * A 16-bit unsigned integer that defines whether the Colors field of the |
| * DIB contains explicit RGB values or indexes into a palette. |
| */ |
| private ColorUsage colorUsage; |
| /** |
| * A 16-bit unsigned integer that defines the number of scan lines in the source. |
| */ |
| private int scanCount; |
| /** |
| * A 16-bit unsigned integer that defines the starting scan line in the source. |
| */ |
| private int startScan; |
| |
| /** the source rectangle */ |
| protected final Rectangle2D srcBounds = new Rectangle2D.Double(); |
| |
| /** the destination rectangle, having the same dimension as the source rectangle */ |
| protected final Rectangle2D dstBounds = new Rectangle2D.Double(); |
| |
| /** |
| * A variable-sized DeviceIndependentBitmap Object that is the source of the color data. |
| */ |
| private HwmfBitmapDib dib; |
| |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.setDibToDev; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| colorUsage = ColorUsage.valueOf(leis.readUShort()); |
| scanCount = leis.readUShort(); |
| startScan = leis.readUShort(); |
| |
| int size = 3*LittleEndianConsts.SHORT_SIZE; |
| |
| final Point2D srcPnt = new Point2D.Double(); |
| size += readPointS(leis, srcPnt); |
| |
| size += readBounds2(leis, dstBounds); |
| |
| dib = new HwmfBitmapDib(); |
| size += dib.init(leis, (int)(recordSize-6-size)); |
| |
| srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight()); |
| |
| return size; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| ctx.addObjectTableEntry(this); |
| } |
| |
| @Override |
| public void applyObject(HwmfGraphics ctx) { |
| |
| } |
| |
| @Override |
| public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) { |
| return dib.getImage(foreground,background,hasAlpha); |
| } |
| |
| @Override |
| public byte[] getBMPData() { |
| return dib.getBMPData(); |
| } |
| } |
| |
| |
| public static class WmfDibBitBlt extends WmfDibStretchBlt { |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.dibBitBlt; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3)); |
| |
| int rasterOpCode = leis.readUShort(); |
| int rasterOpIndex = leis.readUShort(); |
| |
| rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); |
| assert(rasterOpCode == rasterOperation.opCode); |
| |
| int size = 2*LittleEndianConsts.SHORT_SIZE; |
| |
| final Point2D srcPnt = new Point2D.Double(); |
| size += readPointS(leis, srcPnt); |
| if (!hasBitmap) { |
| /*int reserved =*/ leis.readShort(); |
| size += LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| size += readBounds2(leis, dstBounds); |
| if (hasBitmap) { |
| target = new HwmfBitmapDib(); |
| size += target.init(leis, (int)(recordSize-6-size)); |
| } |
| |
| // the destination rectangle, having the same dimension as the source rectangle |
| srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight()); |
| |
| return size; |
| } |
| } |
| |
| public static class WmfDibStretchBlt implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry { |
| /** |
| * A 32-bit unsigned integer that defines how the source pixels, the current brush |
| * in the playback device context, and the destination pixels are to be combined to form the |
| * new image. This code MUST be one of the values in the Ternary Raster Operation Enumeration. |
| */ |
| protected HwmfTernaryRasterOp rasterOperation; |
| |
| /** the source rectangle */ |
| protected final Rectangle2D srcBounds = new Rectangle2D.Double(); |
| |
| /** the destination rectangle */ |
| protected final Rectangle2D dstBounds = new Rectangle2D.Double(); |
| |
| /** |
| * A variable-sized DeviceIndependentBitmap Object that defines image content. |
| * This object MUST be specified, even if the raster operation does not require a source. |
| */ |
| protected HwmfBitmapDib target; |
| |
| @Override |
| public HwmfRecordType getWmfRecordType() { |
| return HwmfRecordType.dibStretchBlt; |
| } |
| |
| @Override |
| public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException { |
| boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3)); |
| |
| int rasterOpCode = leis.readUShort(); |
| int rasterOpIndex = leis.readUShort(); |
| |
| rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex); |
| assert(rasterOpCode == rasterOperation.opCode); |
| |
| int size = 2*LittleEndianConsts.SHORT_SIZE; |
| |
| size += readBounds2(leis, srcBounds); |
| if (!hasBitmap) { |
| /*int reserved =*/ leis.readShort(); |
| size += LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| size += readBounds2(leis, dstBounds); |
| if (hasBitmap) { |
| target = new HwmfBitmapDib(); |
| size += target.init(leis, (int)(recordSize-6-size)); |
| } |
| |
| return size; |
| } |
| |
| @Override |
| public void draw(HwmfGraphics ctx) { |
| ctx.addObjectTableEntry(this); |
| } |
| |
| @Override |
| public void applyObject(HwmfGraphics ctx) { |
| |
| } |
| |
| @Override |
| public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) { |
| return (target != null && target.isValid()) ? target.getImage(foreground,background,hasAlpha) : null; |
| } |
| |
| @Override |
| public byte[] getBMPData() { |
| return (target != null && target.isValid()) ? target.getBMPData() : null; |
| } |
| } |
| |
| static int readBounds2(LittleEndianInputStream leis, Rectangle2D bounds) { |
| /** |
| * The 16-bit signed integers that defines the corners of the bounding rectangle. |
| */ |
| int h = leis.readShort(); |
| int w = leis.readShort(); |
| int y = leis.readShort(); |
| int x = leis.readShort(); |
| |
| bounds.setRect(x, y, w, h); |
| |
| return 4 * LittleEndianConsts.SHORT_SIZE; |
| } |
| |
| } |