/*
 * 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.pdfbox.pdmodel.graphics.blend;

import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;

/**
 * AWT Paint that adds a soft mask to the alpha channel of the existing parent paint. If the parent
 * paint does not have an alpha channel, a new raster is created.
 * 
 * @author Khn & Weyh Software, GmbH
 */
public final class SoftMaskPaint implements Paint
{
    private final Paint parentPaint;
    private final Raster softMaskRaster;

    /**
     * Applies the soft mask to the parent.
     */
    public SoftMaskPaint(Paint parentPaint, Raster softMaskRaster)
    {
        this.parentPaint = parentPaint;
        this.softMaskRaster = softMaskRaster;
    }

    public int getTransparency()
    {
        return Transparency.TRANSLUCENT;
    }

    public PaintContext createContext(ColorModel cm, Rectangle deviceBounds,
            Rectangle2D userBounds, AffineTransform at, RenderingHints hints)
    {
        try
        {
            PaintContext parentContext = parentPaint.createContext(null, deviceBounds, userBounds,
                    at, hints);
            return new Context(parentContext);
        }
        catch (IOException e)
        {
            return null; // context cannot be created
        }
    }

    private class Context implements PaintContext
    {
        private PaintContext parentContext;
        private final ColorModel colorModel;
        private final int numColorComponents;
        private final ColorModel parentColorModel;

        Context(PaintContext parentContext) throws IOException
        {
            this.parentContext = parentContext;
            parentColorModel = parentContext.getColorModel();
            if (parentContext.getColorModel().hasAlpha())
            {
                colorModel = parentColorModel;
            }
            else
            {
                colorModel = new ComponentColorModel(parentContext.getColorModel()
                        .getColorSpace(), true, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
            }
            numColorComponents = colorModel.getNumColorComponents();
        }

        public ColorModel getColorModel()
        {
            return colorModel;
        }

        public Raster getRaster(int x, int y, int w, int h)
        {
            Raster parentRaster = parentContext.getRaster(x, y, w, h);

            // getRaster can return the raster with origin (0,0) even if we applied for (x,y)
            int parentMinX = parentRaster.getMinX();
            int parentMinY = parentRaster.getMinY();

            WritableRaster result;
            if (parentRaster instanceof WritableRaster)
            {
                if (parentColorModel.equals(colorModel))
                {
                    result = parentRaster.createCompatibleWritableRaster();
                    result.setDataElements(-parentMinX, -parentMinY, parentRaster);
                }
                else
                {
                    BufferedImage parentImage = new BufferedImage(parentColorModel,
                            (WritableRaster) parentRaster,
                            parentColorModel.isAlphaPremultiplied(), null);
                    result = Raster.createWritableRaster(
                            colorModel.createCompatibleSampleModel(w, h), new Point(0, 0));
                    BufferedImage resultImage = new BufferedImage(colorModel, result, false, null);
                    resultImage.getGraphics().drawImage(parentImage, 0, 0, null);
                }
            }
            else
            {
                result = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, w, h, getColorModel()
                        .getNumComponents(), new Point(0, 0));
                ColorConvertOp colorConvertOp = new ColorConvertOp(
                        parentColorModel.getColorSpace(), colorModel.getColorSpace(), null);
                colorConvertOp.filter(parentRaster, result);
            }

            int softMaskMinX = softMaskRaster.getMinX();
            int softMaskMinY = softMaskRaster.getMinY();
            int softMaskMaxX = softMaskMinX + softMaskRaster.getWidth();
            int softMaskMaxY = softMaskMinY + softMaskRaster.getHeight();

            for (int j = 0; j < h; j++)
            {
                for (int i = 0; i < w; i++)
                {
                    int rx = x + i;
                    int ry = y + j;

                    int alpha;
                    if ((rx >= softMaskMinX) && (rx < softMaskMaxX) && (ry >= softMaskMinY)
                            && (ry < softMaskMaxY))
                    {
                        alpha = softMaskRaster.getSample(Math.round(rx), Math.round(ry), 0);
                    }
                    else
                    {
                        alpha = 0;
                    }
                    alpha = alpha * result.getSample(i, j, numColorComponents) / 255;
                    result.setSample(i, j, numColorComponents, alpha);
                }
            }

            return result;
        }

        public void dispose()
        {
            // do nothing
        }
    }
}

@@ -1553,16 +1553,17 @@
 author K
+%C3%BC
 hn & Wey
@@ -1579,16 +1579,16 @@
 e, GmbH%0A
-
  */%0Apubl
@@ -1932,32 +1932,46 @@
 kRaster;%0A    %7D%0A%0A
+    @Override%0A
     public int g
@@ -2037,24 +2037,38 @@
 ENT;%0A    %7D%0A%0A
+    @Override%0A
     public P
@@ -2609,16 +2609,22 @@
 private 
+final 
 PaintCon
@@ -3401,32 +3401,50 @@
 s();%0A        %7D%0A%0A
+        @Override%0A
         public C
@@ -3513,32 +3513,50 @@
 del;%0A        %7D%0A%0A
+        @Override%0A
         public R
@@ -6002,38 +6002,14 @@
 ple(
-Math.round(
 rx
-)
 , 
-Math.round(
 ry
-)
 , 0)
@@ -6356,32 +6356,50 @@
 ult;%0A        %7D%0A%0A
+        @Override%0A
         public v

remove Math.round() on an int