You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hwmf.record;
  16. import static org.apache.poi.hwmf.record.HwmfDraw.boundsToString;
  17. import static org.apache.poi.hwmf.record.HwmfDraw.readPointS;
  18. import java.awt.Color;
  19. import java.awt.Shape;
  20. import java.awt.geom.Path2D;
  21. import java.awt.geom.Point2D;
  22. import java.awt.geom.Rectangle2D;
  23. import java.awt.image.BufferedImage;
  24. import java.io.IOException;
  25. import org.apache.poi.hwmf.draw.HwmfDrawProperties;
  26. import org.apache.poi.hwmf.draw.HwmfGraphics;
  27. import org.apache.poi.hwmf.record.HwmfMisc.WmfSetBkMode.HwmfBkMode;
  28. import org.apache.poi.util.LittleEndianConsts;
  29. import org.apache.poi.util.LittleEndianInputStream;
  30. public class HwmfFill {
  31. /**
  32. * A record which contains an image (to be extracted)
  33. */
  34. public interface HwmfImageRecord {
  35. default BufferedImage getImage() {
  36. return getImage(Color.BLACK, new Color(0x00FFFFFF, true), true);
  37. }
  38. /**
  39. * Provide an image using the fore-/background color, in case of a 1-bit pattern
  40. * @param foreground the foreground color
  41. * @param background the background color
  42. * @param hasAlpha if true, the background color is rendered transparent - see {@link HwmfMisc.WmfSetBkMode.HwmfBkMode}
  43. * @return the image
  44. *
  45. * @since POI 4.1.1
  46. */
  47. BufferedImage getImage(Color foreground, Color background, boolean hasAlpha);
  48. /**
  49. * @return the raw BMP data
  50. *
  51. * @see <a href="https://en.wikipedia.org/wiki/BMP_file_format">BMP format</a>
  52. * @since POI 4.1.1
  53. */
  54. byte[] getBMPData();
  55. }
  56. /**
  57. * The ColorUsage Enumeration (a 16-bit unsigned integer) specifies whether a color table
  58. * exists in a device-independent bitmap (DIB) and how to interpret its values,
  59. * i.e. if contains explicit RGB values or indexes into a palette.
  60. */
  61. public enum ColorUsage {
  62. /**
  63. * The color table contains RGB values
  64. */
  65. DIB_RGB_COLORS(0x0000),
  66. /**
  67. * The color table contains 16-bit indices into the current logical palette in
  68. * the playback device context.
  69. */
  70. DIB_PAL_COLORS(0x0001),
  71. /**
  72. * No color table exists. The pixels in the DIB are indices into the current
  73. * logical palette in the playback device context.
  74. */
  75. DIB_PAL_INDICES(0x0002)
  76. ;
  77. public final int flag;
  78. ColorUsage(int flag) {
  79. this.flag = flag;
  80. }
  81. public static ColorUsage valueOf(int flag) {
  82. for (ColorUsage bs : values()) {
  83. if (bs.flag == flag) return bs;
  84. }
  85. return null;
  86. }
  87. }
  88. /**
  89. * The META_FILLREGION record fills a region using a specified brush.
  90. */
  91. public static class WmfFillRegion implements HwmfRecord {
  92. /**
  93. * A 16-bit unsigned integer used to index into the WMF Object Table to get
  94. * the region to be filled.
  95. */
  96. protected int regionIndex;
  97. /**
  98. * A 16-bit unsigned integer used to index into the WMF Object Table to get the
  99. * brush to use for filling the region.
  100. */
  101. protected int brushIndex;
  102. @Override
  103. public HwmfRecordType getWmfRecordType() {
  104. return HwmfRecordType.fillRegion;
  105. }
  106. @Override
  107. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  108. regionIndex = leis.readUShort();
  109. brushIndex = leis.readUShort();
  110. return 2*LittleEndianConsts.SHORT_SIZE;
  111. }
  112. @Override
  113. public void draw(HwmfGraphics ctx) {
  114. ctx.applyObjectTableEntry(regionIndex);
  115. ctx.applyObjectTableEntry(brushIndex);
  116. Shape region = ctx.getProperties().getRegion();
  117. if (region != null) {
  118. ctx.fill(region);
  119. }
  120. }
  121. }
  122. /**
  123. * The META_PAINTREGION record paints the specified region by using the brush that is
  124. * defined in the playback device context.
  125. */
  126. public static class WmfPaintRegion implements HwmfRecord {
  127. /**
  128. * A 16-bit unsigned integer used to index into the WMF Object Table to get
  129. * the region to be painted.
  130. */
  131. int regionIndex;
  132. public HwmfRecordType getWmfRecordType() {
  133. return HwmfRecordType.paintRegion;
  134. }
  135. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  136. regionIndex = leis.readUShort();
  137. return LittleEndianConsts.SHORT_SIZE;
  138. }
  139. @Override
  140. public void draw(HwmfGraphics ctx) {
  141. ctx.applyObjectTableEntry(regionIndex);
  142. Shape region = ctx.getProperties().getRegion();
  143. if (region != null) {
  144. ctx.fill(region);
  145. }
  146. }
  147. }
  148. /**
  149. * The META_FLOODFILL record fills an area of the output surface with the brush that
  150. * is defined in the playback device context.
  151. */
  152. public static class WmfFloodFill implements HwmfRecord {
  153. /**
  154. * A 32-bit ColorRef Object that defines the color value.
  155. */
  156. protected final HwmfColorRef colorRef = new HwmfColorRef();
  157. /** the point where filling is to start. */
  158. protected final Point2D start = new Point2D.Double();
  159. @Override
  160. public HwmfRecordType getWmfRecordType() {
  161. return HwmfRecordType.floodFill;
  162. }
  163. @Override
  164. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  165. int size = colorRef.init(leis);
  166. size += readPointS(leis, start);
  167. return size;
  168. }
  169. @Override
  170. public void draw(HwmfGraphics ctx) {
  171. }
  172. }
  173. /**
  174. * The META_SETPOLYFILLMODE record sets polygon fill mode in the playback device context for
  175. * graphics operations that fill polygons.
  176. */
  177. public static class WmfSetPolyfillMode implements HwmfRecord {
  178. /**
  179. * A 16-bit unsigned integer that defines polygon fill mode.
  180. * This MUST be one of the values: ALTERNATE = 0x0001, WINDING = 0x0002
  181. */
  182. public enum HwmfPolyfillMode {
  183. /**
  184. * Selects alternate mode (fills the area between odd-numbered and
  185. * even-numbered polygon sides on each scan line).
  186. */
  187. ALTERNATE(0x0001, Path2D.WIND_EVEN_ODD),
  188. /**
  189. * Selects winding mode (fills any region with a nonzero winding value).
  190. */
  191. WINDING(0x0002, Path2D.WIND_NON_ZERO);
  192. public final int wmfFlag;
  193. public final int awtFlag;
  194. HwmfPolyfillMode(int wmfFlag, int awtFlag) {
  195. this.wmfFlag = wmfFlag;
  196. this.awtFlag = awtFlag;
  197. }
  198. public static HwmfPolyfillMode valueOf(int wmfFlag) {
  199. for (HwmfPolyfillMode pm : values()) {
  200. if (pm.wmfFlag == wmfFlag) return pm;
  201. }
  202. return null;
  203. }
  204. }
  205. /**
  206. * An unsigned integer that defines polygon fill mode.
  207. * This MUST be one of the values: ALTERNATE = 0x0001, WINDING = 0x0002
  208. */
  209. protected HwmfPolyfillMode polyFillMode;
  210. @Override
  211. public HwmfRecordType getWmfRecordType() {
  212. return HwmfRecordType.setPolyFillMode;
  213. }
  214. @Override
  215. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  216. polyFillMode = HwmfPolyfillMode.valueOf(leis.readUShort() & 3);
  217. return LittleEndianConsts.SHORT_SIZE;
  218. }
  219. @Override
  220. public void draw(HwmfGraphics ctx) {
  221. ctx.getProperties().setPolyfillMode(polyFillMode);
  222. }
  223. @Override
  224. public String toString() {
  225. return "{ polyFillMode: '"+ polyFillMode +"' }";
  226. }
  227. }
  228. /**
  229. * The META_EXTFLOODFILL record fills an area with the brush that is defined in
  230. * the playback device context.
  231. */
  232. public static class WmfExtFloodFill extends WmfFloodFill {
  233. /**
  234. * A 16-bit unsigned integer that defines the fill operation to be performed. This
  235. * member MUST be one of the values in the FloodFill Enumeration table:
  236. *
  237. * FLOODFILLBORDER = 0x0000:
  238. * The fill area is bounded by the color specified by the Color member.
  239. * This style is identical to the filling performed by the META_FLOODFILL record.
  240. *
  241. * FLOODFILLSURFACE = 0x0001:
  242. * The fill area is bounded by the color that is specified by the Color member.
  243. * Filling continues outward in all directions as long as the color is encountered.
  244. * This style is useful for filling areas with multicolored boundaries.
  245. */
  246. protected int mode;
  247. @Override
  248. public HwmfRecordType getWmfRecordType() {
  249. return HwmfRecordType.extFloodFill;
  250. }
  251. @Override
  252. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  253. mode = leis.readUShort();
  254. return super.init(leis, recordSize, recordFunction)+LittleEndianConsts.SHORT_SIZE;
  255. }
  256. @Override
  257. public void draw(HwmfGraphics ctx) {
  258. }
  259. }
  260. /**
  261. * The META_INVERTREGION record draws a region in which the colors are inverted.
  262. */
  263. public static class WmfInvertRegion implements HwmfRecord {
  264. /**
  265. * A 16-bit unsigned integer used to index into the WMF Object Table to get
  266. * the region to be inverted.
  267. */
  268. private int region;
  269. @Override
  270. public HwmfRecordType getWmfRecordType() {
  271. return HwmfRecordType.invertRegion;
  272. }
  273. @Override
  274. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  275. region = leis.readUShort();
  276. return LittleEndianConsts.SHORT_SIZE;
  277. }
  278. @Override
  279. public void draw(HwmfGraphics ctx) {
  280. }
  281. }
  282. /**
  283. * The META_PATBLT record paints a specified rectangle using the brush that is defined in the playback
  284. * device context. The brush color and the surface color or colors are combined using the specified
  285. * raster operation.
  286. */
  287. public static class WmfPatBlt implements HwmfRecord {
  288. /**
  289. * A 32-bit unsigned integer that defines the raster operation code.
  290. * This code MUST be one of the values in the Ternary Raster Operation enumeration table.
  291. */
  292. private HwmfTernaryRasterOp rasterOperation;
  293. private final Rectangle2D bounds = new Rectangle2D.Double();
  294. @Override
  295. public HwmfRecordType getWmfRecordType() {
  296. return HwmfRecordType.patBlt;
  297. }
  298. @Override
  299. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  300. int rasterOpCode = leis.readUShort();
  301. int rasterOpIndex = leis.readUShort();
  302. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
  303. assert(rasterOpCode == rasterOperation.opCode);
  304. return readBounds2(leis, bounds)+2*LittleEndianConsts.SHORT_SIZE;
  305. }
  306. @Override
  307. public void draw(HwmfGraphics ctx) {
  308. }
  309. }
  310. /**
  311. * The META_STRETCHBLT record specifies the transfer of a block of pixels according to a raster
  312. * operation, with possible expansion or contraction.
  313. * The destination of the transfer is the current output region in the playback device context.
  314. * There are two forms of META_STRETCHBLT, one which specifies a bitmap as the source, and the other
  315. * which uses the playback device context as the source. Definitions follow for the fields that are the
  316. * same in the two forms of META_STRETCHBLT are defined below. The subsections that follow specify
  317. * the packet structures of the two forms of META_STRETCHBLT.
  318. * The expansion or contraction is performed according to the stretching mode currently set in the
  319. * playback device context, which MUST be a value from the StretchMode.
  320. */
  321. public static class WmfStretchBlt implements HwmfRecord {
  322. /**
  323. * A 32-bit unsigned integer that defines how the source pixels, the current brush
  324. * in the playback device context, and the destination pixels are to be combined to form the new
  325. * image. This code MUST be one of the values in the Ternary Raster Operation Enumeration
  326. */
  327. protected HwmfTernaryRasterOp rasterOperation;
  328. /** the source rectangle */
  329. protected final Rectangle2D srcBounds = new Rectangle2D.Double();
  330. /** the destination rectangle */
  331. protected final Rectangle2D dstBounds = new Rectangle2D.Double();
  332. /**
  333. * A variable-sized Bitmap16 Object that defines source image content.
  334. * This object MUST be specified, even if the raster operation does not require a source.
  335. */
  336. protected HwmfBitmap16 target;
  337. @Override
  338. public HwmfRecordType getWmfRecordType() {
  339. return HwmfRecordType.stretchBlt;
  340. }
  341. @Override
  342. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  343. boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
  344. int rasterOpCode = leis.readUShort();
  345. int rasterOpIndex = leis.readUShort();
  346. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
  347. assert(rasterOpCode == rasterOperation.opCode);
  348. int size = 2*LittleEndianConsts.SHORT_SIZE;
  349. size += readBounds2(leis, srcBounds);
  350. if (!hasBitmap) {
  351. /*int reserved =*/ leis.readShort();
  352. size += LittleEndianConsts.SHORT_SIZE;
  353. }
  354. size += readBounds2(leis, dstBounds);
  355. if (hasBitmap) {
  356. target = new HwmfBitmap16();
  357. size += target.init(leis);
  358. }
  359. return size;
  360. }
  361. @Override
  362. public void draw(HwmfGraphics ctx) {
  363. }
  364. @Override
  365. public String toString() {
  366. return
  367. "{ rasterOperation: '"+rasterOperation+"'"+
  368. ", srcBounds: "+boundsToString(srcBounds)+
  369. ", dstBounds: "+boundsToString(dstBounds)+
  370. "}";
  371. }
  372. }
  373. /**
  374. * The META_STRETCHDIB record specifies the transfer of color data from a
  375. * block of pixels in device independent format according to a raster operation,
  376. * with possible expansion or contraction.
  377. * The source of the color data is a DIB, and the destination of the transfer is
  378. * the current output region in the playback device context.
  379. */
  380. public static class WmfStretchDib implements HwmfRecord, HwmfImageRecord {
  381. /**
  382. * A 32-bit unsigned integer that defines how the source pixels, the current brush in
  383. * the playback device context, and the destination pixels are to be combined to
  384. * form the new image.
  385. */
  386. protected HwmfTernaryRasterOp rasterOperation;
  387. /**
  388. * A 16-bit unsigned integer that defines whether the Colors field of the
  389. * DIB contains explicit RGB values or indexes into a palette.
  390. */
  391. protected ColorUsage colorUsage;
  392. /** the source rectangle. */
  393. protected final Rectangle2D srcBounds = new Rectangle2D.Double();
  394. /** the destination rectangle. */
  395. protected final Rectangle2D dstBounds = new Rectangle2D.Double();
  396. /**
  397. * A variable-sized DeviceIndependentBitmap Object (section 2.2.2.9) that is the
  398. * source of the color data.
  399. */
  400. protected final HwmfBitmapDib bitmap = new HwmfBitmapDib();
  401. @Override
  402. public HwmfRecordType getWmfRecordType() {
  403. return HwmfRecordType.stretchDib;
  404. }
  405. @Override
  406. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  407. int rasterOpCode = leis.readUShort();
  408. int rasterOpIndex = leis.readUShort();
  409. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
  410. assert(rasterOpCode == rasterOperation.opCode);
  411. colorUsage = ColorUsage.valueOf(leis.readUShort());
  412. int size = 3*LittleEndianConsts.SHORT_SIZE;
  413. size += readBounds2(leis, srcBounds);
  414. size += readBounds2(leis, dstBounds);
  415. size += bitmap.init(leis, (int)(recordSize-6-size));
  416. return size;
  417. }
  418. @Override
  419. public void draw(HwmfGraphics ctx) {
  420. HwmfDrawProperties prop = ctx.getProperties();
  421. prop.setRasterOp(rasterOperation);
  422. if (bitmap.isValid()) {
  423. BufferedImage bi = bitmap.getImage(prop.getPenColor().getColor(), prop.getBackgroundColor().getColor(),
  424. prop.getBkMode() == HwmfBkMode.TRANSPARENT);
  425. ctx.drawImage(bi, srcBounds, dstBounds);
  426. } else if (!dstBounds.isEmpty()) {
  427. BufferedImage bi = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
  428. ctx.drawImage(bi, new Rectangle2D.Double(0,0,100,100), dstBounds);
  429. }
  430. }
  431. @Override
  432. public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
  433. return bitmap.getImage(foreground,background,hasAlpha);
  434. }
  435. public HwmfBitmapDib getBitmap() {
  436. return bitmap;
  437. }
  438. @Override
  439. public byte[] getBMPData() {
  440. return bitmap.getBMPData();
  441. }
  442. @Override
  443. public String toString() {
  444. return
  445. "{ rasterOperation: '"+rasterOperation+"'"+
  446. ", colorUsage: '"+colorUsage+"'"+
  447. ", srcBounds: "+boundsToString(srcBounds)+
  448. ", dstBounds: "+boundsToString(dstBounds)+
  449. "}";
  450. }
  451. }
  452. public static class WmfBitBlt extends WmfStretchBlt {
  453. @Override
  454. public HwmfRecordType getWmfRecordType() {
  455. return HwmfRecordType.bitBlt;
  456. }
  457. @Override
  458. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  459. boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3));
  460. int rasterOpCode = leis.readUShort();
  461. int rasterOpIndex = leis.readUShort();
  462. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
  463. assert(rasterOpCode == rasterOperation.opCode);
  464. int size = 2*LittleEndianConsts.SHORT_SIZE;
  465. final Point2D srcPnt = new Point2D.Double();
  466. size += readPointS(leis, srcPnt);
  467. if (!hasBitmap) {
  468. /*int reserved =*/ leis.readShort();
  469. size += LittleEndianConsts.SHORT_SIZE;
  470. }
  471. size += readBounds2(leis, dstBounds);
  472. if (hasBitmap) {
  473. target = new HwmfBitmap16();
  474. size += target.init(leis);
  475. }
  476. srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight());
  477. return size;
  478. }
  479. }
  480. /**
  481. * The META_SETDIBTODEV record sets a block of pixels in the playback device context
  482. * using deviceindependent color data.
  483. * The source of the color data is a DIB
  484. */
  485. public static class WmfSetDibToDev implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
  486. /**
  487. * A 16-bit unsigned integer that defines whether the Colors field of the
  488. * DIB contains explicit RGB values or indexes into a palette.
  489. */
  490. private ColorUsage colorUsage;
  491. /**
  492. * A 16-bit unsigned integer that defines the number of scan lines in the source.
  493. */
  494. private int scanCount;
  495. /**
  496. * A 16-bit unsigned integer that defines the starting scan line in the source.
  497. */
  498. private int startScan;
  499. /** the source rectangle */
  500. protected final Rectangle2D srcBounds = new Rectangle2D.Double();
  501. /** the destination rectangle, having the same dimension as the source rectangle */
  502. protected final Rectangle2D dstBounds = new Rectangle2D.Double();
  503. /**
  504. * A variable-sized DeviceIndependentBitmap Object that is the source of the color data.
  505. */
  506. private HwmfBitmapDib dib;
  507. @Override
  508. public HwmfRecordType getWmfRecordType() {
  509. return HwmfRecordType.setDibToDev;
  510. }
  511. @Override
  512. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  513. colorUsage = ColorUsage.valueOf(leis.readUShort());
  514. scanCount = leis.readUShort();
  515. startScan = leis.readUShort();
  516. int size = 3*LittleEndianConsts.SHORT_SIZE;
  517. final Point2D srcPnt = new Point2D.Double();
  518. size += readPointS(leis, srcPnt);
  519. size += readBounds2(leis, dstBounds);
  520. dib = new HwmfBitmapDib();
  521. size += dib.init(leis, (int)(recordSize-6-size));
  522. srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight());
  523. return size;
  524. }
  525. @Override
  526. public void draw(HwmfGraphics ctx) {
  527. ctx.addObjectTableEntry(this);
  528. }
  529. @Override
  530. public void applyObject(HwmfGraphics ctx) {
  531. }
  532. @Override
  533. public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
  534. return dib.getImage(foreground,background,hasAlpha);
  535. }
  536. @Override
  537. public byte[] getBMPData() {
  538. return dib.getBMPData();
  539. }
  540. }
  541. public static class WmfDibBitBlt extends WmfDibStretchBlt {
  542. @Override
  543. public HwmfRecordType getWmfRecordType() {
  544. return HwmfRecordType.dibBitBlt;
  545. }
  546. @Override
  547. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  548. boolean hasBitmap = (recordSize/2 != ((recordFunction >> 8) + 3));
  549. int rasterOpCode = leis.readUShort();
  550. int rasterOpIndex = leis.readUShort();
  551. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
  552. assert(rasterOpCode == rasterOperation.opCode);
  553. int size = 2*LittleEndianConsts.SHORT_SIZE;
  554. final Point2D srcPnt = new Point2D.Double();
  555. size += readPointS(leis, srcPnt);
  556. if (!hasBitmap) {
  557. /*int reserved =*/ leis.readShort();
  558. size += LittleEndianConsts.SHORT_SIZE;
  559. }
  560. size += readBounds2(leis, dstBounds);
  561. if (hasBitmap) {
  562. target = new HwmfBitmapDib();
  563. size += target.init(leis, (int)(recordSize-6-size));
  564. }
  565. // the destination rectangle, having the same dimension as the source rectangle
  566. srcBounds.setRect(srcPnt.getX(), srcPnt.getY(), dstBounds.getWidth(), dstBounds.getHeight());
  567. return size;
  568. }
  569. }
  570. public static class WmfDibStretchBlt implements HwmfRecord, HwmfImageRecord, HwmfObjectTableEntry {
  571. /**
  572. * A 32-bit unsigned integer that defines how the source pixels, the current brush
  573. * in the playback device context, and the destination pixels are to be combined to form the
  574. * new image. This code MUST be one of the values in the Ternary Raster Operation Enumeration.
  575. */
  576. protected HwmfTernaryRasterOp rasterOperation;
  577. /** the source rectangle */
  578. protected final Rectangle2D srcBounds = new Rectangle2D.Double();
  579. /** the destination rectangle */
  580. protected final Rectangle2D dstBounds = new Rectangle2D.Double();
  581. /**
  582. * A variable-sized DeviceIndependentBitmap Object that defines image content.
  583. * This object MUST be specified, even if the raster operation does not require a source.
  584. */
  585. protected HwmfBitmapDib target;
  586. @Override
  587. public HwmfRecordType getWmfRecordType() {
  588. return HwmfRecordType.dibStretchBlt;
  589. }
  590. @Override
  591. public int init(LittleEndianInputStream leis, long recordSize, int recordFunction) throws IOException {
  592. boolean hasBitmap = (recordSize > ((recordFunction >> 8) + 3));
  593. int rasterOpCode = leis.readUShort();
  594. int rasterOpIndex = leis.readUShort();
  595. rasterOperation = HwmfTernaryRasterOp.valueOf(rasterOpIndex);
  596. assert(rasterOpCode == rasterOperation.opCode);
  597. int size = 2*LittleEndianConsts.SHORT_SIZE;
  598. size += readBounds2(leis, srcBounds);
  599. if (!hasBitmap) {
  600. /*int reserved =*/ leis.readShort();
  601. size += LittleEndianConsts.SHORT_SIZE;
  602. }
  603. size += readBounds2(leis, dstBounds);
  604. if (hasBitmap) {
  605. target = new HwmfBitmapDib();
  606. size += target.init(leis, (int)(recordSize-6-size));
  607. }
  608. return size;
  609. }
  610. @Override
  611. public void draw(HwmfGraphics ctx) {
  612. ctx.addObjectTableEntry(this);
  613. }
  614. @Override
  615. public void applyObject(HwmfGraphics ctx) {
  616. }
  617. @Override
  618. public BufferedImage getImage(Color foreground, Color background, boolean hasAlpha) {
  619. return (target != null && target.isValid()) ? target.getImage(foreground,background,hasAlpha) : null;
  620. }
  621. @Override
  622. public byte[] getBMPData() {
  623. return (target != null && target.isValid()) ? target.getBMPData() : null;
  624. }
  625. }
  626. static int readBounds2(LittleEndianInputStream leis, Rectangle2D bounds) {
  627. /**
  628. * The 16-bit signed integers that defines the corners of the bounding rectangle.
  629. */
  630. int h = leis.readShort();
  631. int w = leis.readShort();
  632. int y = leis.readShort();
  633. int x = leis.readShort();
  634. bounds.setRect(x, y, w, h);
  635. return 4 * LittleEndianConsts.SHORT_SIZE;
  636. }
  637. }