Skip to content

Commit

Permalink
FOP-3194: Add image mask option for AFP
Browse files Browse the repository at this point in the history
  • Loading branch information
simonsteiner1984 committed Jul 24, 2024
1 parent f9404c6 commit 1ce1125
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public class AFPPaintingState extends org.apache.fop.util.AbstractPaintingState

/** use FS45 images*/
private boolean fs45;
private boolean maskEnabled;

/** the current page */
private transient AFPPagePaintingState pagePaintingState;
Expand Down Expand Up @@ -443,6 +444,13 @@ public void setFS45(boolean fs45) {
}


public boolean isMaskEnabled() {
return maskEnabled;
}

public void setMaskEnabled(boolean maskEnabled) {
this.maskEnabled = maskEnabled;
}

/** {@inheritDoc} */
@Override
Expand Down
5 changes: 4 additions & 1 deletion fop-core/src/main/java/org/apache/fop/afp/ioca/Tile.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ private void writeData(OutputStream os) throws IOException {
0x00 // reserved
};
final int lengthOffset = 2;
if (ideSize == 24) {
if (ideSize <= 8) {
dataHeader[4] = (byte) 0x01;
writeChunksToStream(data, dataHeader, lengthOffset, MAX_DATA_LEN, os);
} else if (ideSize == 24) {
byte[] red = new byte[data.length / 3];
byte[] green = new byte[data.length / 3];
byte[] blue = new byte[data.length / 3];
Expand Down
10 changes: 7 additions & 3 deletions fop-core/src/main/java/org/apache/fop/afp/modca/ImageObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,13 @@ public void setViewport(AFPDataObjectInfo dataObjectInfo) {
ImageDataDescriptor imageDataDescriptor
= factory.createImageDataDescriptor(dataWidth, dataHeight, dataWidthRes, dataHeightRes);

if (MimeConstants.MIME_AFP_IOCA_FS45.equals(imageObjectInfo.getMimeType())) {
imageDataDescriptor.setFunctionSet(ImageDataDescriptor.FUNCTION_SET_FS45);
if (imageObjectInfo.getBitsPerPixel() == 32) {
boolean hasTransparencyMask = imageObjectInfo.getTransparencyMask() != null;
boolean fs45 = MimeConstants.MIME_AFP_IOCA_FS45.equals(imageObjectInfo.getMimeType());
if (hasTransparencyMask || fs45) {
if (fs45) {
imageDataDescriptor.setFunctionSet(ImageDataDescriptor.FUNCTION_SET_FS45);
}
if (hasTransparencyMask || imageObjectInfo.getBitsPerPixel() == 32) {
Tile tile = factory.createTile();
TilePosition tilePosition = factory.createTilePosition();
TileSize tileSize = factory.createTileSize(dataWidth, dataHeight, dataWidthRes, dataHeightRes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,10 @@ public void setFS45(boolean fs45) {
paintingState.setFS45(fs45);
}

public void setMaskEnabled(boolean maskEnabled) {
paintingState.setMaskEnabled(maskEnabled);
}

/** {@inheritDoc} */
public boolean getWrapPSeg() {
return paintingState.getWrapPSeg();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@

package org.apache.fop.render.afp;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
Expand Down Expand Up @@ -131,6 +133,12 @@ public void handleImage(RenderingContext context, Image image, Rectangle pos)
boolean included = afpContext.getResourceManager().tryIncludeObject(imageObjectInfo);
if (!included) {
long start = System.currentTimeMillis();
RenderedImage ri = imageRend.getRenderedImage();
if (ri.getColorModel().hasAlpha()) {
byte[] maskImage = buildMaskImage((BufferedImage) ri, afpContext.getPaintingState());
imageObjectInfo.setTransparencyMask(maskImage);
}

//encode only if the same image has not been encoded, yet
encoder.encodeImage(imageObjectInfo, paintingState);
if (log.isDebugEnabled()) {
Expand All @@ -143,6 +151,32 @@ public void handleImage(RenderingContext context, Image image, Rectangle pos)
}
}

private byte[] buildMaskImage(BufferedImage image, AFPPaintingState paintingState) {
if (!paintingState.isMaskEnabled()) {
return null;
}
BufferedImage mask = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
int white = Color.WHITE.getRGB();
boolean noalpha = true;
for (int i = 0; i < image.getWidth(); i++) {
for (int j = 0; j < image.getHeight(); j++) {
int alpha = (image.getRGB(i, j) >> 24) & 0xff;
if (alpha != 0) {
mask.setRGB(i, j, white);
} else {
noalpha = false;
}
}
}
if (noalpha) {
return null;
}
RenderedImage renderedImage =
BitmapImageUtil.convertToMonochrome(mask, new Dimension(mask.getWidth(), mask.getHeight()), 1);
DataBufferByte bufferByte = (DataBufferByte) renderedImage.getData().getDataBuffer();
return bufferByte.getData();
}

/** {@inheritDoc} */
public boolean isCompatible(RenderingContext targetContext, Image image) {
return (image == null || image instanceof ImageRendered)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_FS45;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_JPEG;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MAPPING_OPTION;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MASK_ENABLED;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MODE;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_NATIVE;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_WRAP_PSEG;
Expand Down Expand Up @@ -175,6 +176,10 @@ public Boolean isFs45() {
return getParam(IMAGES_FS45, Boolean.class);
}

public Boolean isMaskEnabled() {
return getParam(IMAGES_MASK_ENABLED, Boolean.class);
}

public Boolean allowJpegEmbedding() {
return getParam(JPEG_ALLOW_JPEG_EMBEDDING, Boolean.class);
}
Expand Down Expand Up @@ -315,6 +320,7 @@ private void configureImages() throws ConfigurationException, FOPException {
setParam(IMAGES_WRAP_PSEG,
imagesCfg.getAttributeAsBoolean(IMAGES_WRAP_PSEG.getName(), false));
setParam(IMAGES_FS45, imagesCfg.getAttributeAsBoolean(IMAGES_FS45.getName(), false));
setParam(IMAGES_MASK_ENABLED, imagesCfg.getAttributeAsBoolean(IMAGES_MASK_ENABLED.getName(), false));
if ("scale-to-fit".equals(imagesCfg.getAttribute(IMAGES_MAPPING_OPTION.getName(), null))) {
setParam(IMAGES_MAPPING_OPTION, MappingOptionTriplet.SCALE_TO_FILL);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ private void configure(AFPDocumentHandler documentHandler, AFPRendererConfig con
if (config.isFs45() != null) {
documentHandler.setFS45(config.isFs45());
}
if (config.isMaskEnabled() != null) {
documentHandler.setMaskEnabled(config.isMaskEnabled());
}
if (config.allowJpegEmbedding() != null) {
documentHandler.canEmbedJpeg(config.allowJpegEmbedding());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum AFPRendererOption implements RendererConfigOption {
IMAGES_JPEG("jpeg", null),
IMAGES_DITHERING_QUALITY("dithering-quality", Float.class),
IMAGES_FS45("fs45", Boolean.class),
IMAGES_MASK_ENABLED("mask-enabled", Boolean.class),
IMAGES_MAPPING_OPTION("mapping_option", Byte.class),
IMAGES_MODE("mode", Boolean.class),
IMAGES_NATIVE("native", Boolean.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@

import java.awt.BasicStroke;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.junit.Assert;
import org.junit.Test;

Expand All @@ -37,6 +42,11 @@
import org.apache.xmlgraphics.java2d.GraphicContext;

import org.apache.fop.afp.modca.GraphicsObject;
import org.apache.fop.afp.modca.ObjectAreaDescriptor;
import org.apache.fop.afp.parser.MODCAParser;
import org.apache.fop.afp.parser.UnparsedStructuredField;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.fonts.FontInfo;

public class AFPGraphics2DTestCase {
Expand All @@ -63,6 +73,57 @@ public void testApplyStroke() {
verify(gObject).setLineWidth(correctedLineWidth);
}

@Test
public void testDrawImageMask() throws IOException {
BufferedImage image = ImageIO.read(new File("test/resources/images/fop-logo-color-palette-8bit.png"));
byte[] data = getAFPField("Data Image", image, true);
ByteArrayInputStream bis = new ByteArrayInputStream(data);
bis.skip(56);
Assert.assertEquals(bis.read(), 0x8E); //start mask
bis.skip(20);
byte[] firstchunk = new byte[5272];
bis.read(firstchunk);
int maskbytes = 0;
for (byte b : firstchunk) {
if (b != 0) {
maskbytes++;
}
}
Assert.assertEquals(maskbytes, 333);
bis.skip(38117 - 57 - 20 - firstchunk.length);
Assert.assertEquals(bis.read(), 0x8F); //end mask
Assert.assertEquals(bis.available(), 302498);
}

private byte[] getAFPField(String field, BufferedImage image, boolean maskEnabled) throws IOException {
FOUserAgent foUserAgent = FopFactory.newInstance(new File(".").toURI()).newFOUserAgent();
AFPResourceManager afpResourceManager = new AFPResourceManager(foUserAgent.getResourceResolver());
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataStream dataStream = afpResourceManager.createDataStream(null, byteArrayOutputStream);
dataStream.startDocument();
dataStream.startPage(1, 1, 0, 1, 1);
AFPPaintingState paintingState = new AFPPaintingState();
paintingState.setMaskEnabled(maskEnabled);
graphics2D = new AFPGraphics2D(false, paintingState, afpResourceManager, resourceInfo, fontInfo);
graphics2D.setGraphicContext(new GraphicContext());
GraphicsObject graphicsObject = new GraphicsObject(new Factory(), null);
graphics2D.setGraphicsObject(graphicsObject);
graphicsObject.getObjectEnvironmentGroup().setObjectAreaDescriptor(new ObjectAreaDescriptor(1, 1, 1, 1));
graphics2D.drawRenderedImage(image, AffineTransform.getTranslateInstance(1000, 1000));
dataStream.endPage();
dataStream.endDocument();
afpResourceManager.writeToStream();
MODCAParser parser = new MODCAParser(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
UnparsedStructuredField structuredField;
while ((structuredField = parser.readNextStructuredField()) != null) {
if (structuredField.toString().contains(field)) {
bos.write(structuredField.getData());
}
}
return bos.toByteArray();
}

@Test
public void testDrawGraphicsFillet() throws IOException {
GraphicContext gc = new GraphicContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_FS45;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_JPEG;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MAPPING_OPTION;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MASK_ENABLED;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_MODE;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_NATIVE;
import static org.apache.fop.render.afp.AFPRendererOption.IMAGES_WRAP_PSEG;
Expand Down Expand Up @@ -151,6 +152,10 @@ public ImagesBuilder setFs45(boolean value) {
return setAttribute(IMAGES_FS45, value);
}

public ImagesBuilder setMaskEnabled(boolean value) {
return setAttribute(IMAGES_MASK_ENABLED, value);
}

public ImagesBuilder setMappingOption(String value) {
return setAttribute(IMAGES_MAPPING_OPTION, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ public void testFS45() throws Exception {
assertEquals(true, conf.isFs45());
}

@Test
public void testMaskEnabled() throws Exception {
parseConfig();
assertEquals(false, conf.isMaskEnabled());
parseConfig(createRenderer().startImages().setMaskEnabled(true).endImages());
assertEquals(true, conf.isMaskEnabled());
}

@Test
public void tesPseg() throws Exception {
parseConfig();
Expand Down

0 comments on commit 1ce1125

Please sign in to comment.