All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.weisj.jsvg.renderer.MaskedPaint Maven / Gradle / Ivy

The newest version!
/*
 * MIT License
 *
 * Copyright (c) 2021-2024 Jannis Weis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package com.github.weisj.jsvg.renderer;

import static com.github.weisj.jsvg.util.ColorUtil.div255;

import java.awt.*;
import java.awt.PaintContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.*;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.github.weisj.jsvg.util.CachedSurfaceSupplier;

public final class MaskedPaint implements Paint, GraphicsUtil.WrappingPaint, GraphicsUtil.DisposablePaint {
    private @NotNull Paint paint;
    private final @NotNull Raster maskRaster;
    private final @NotNull Point maskOffset;
    private final @Nullable CachedSurfaceSupplier.ResourceCleaner cleaner;

    public MaskedPaint(@NotNull Paint paint, @NotNull Raster maskRaster, @NotNull Point2D maskOffset,
            @Nullable CachedSurfaceSupplier.ResourceCleaner cleaner) {
        this.paint = paint;
        this.maskRaster = maskRaster;
        this.maskOffset = new Point((int) Math.floor(maskOffset.getX()), (int) Math.floor(maskOffset.getY()));
        this.cleaner = cleaner;
    }

    @Override
    public void cleanupIfNeeded(@NotNull Output output) {
        if (cleaner != null) cleaner.clean(output);
    }

    @Override
    public void setPaint(@NotNull Paint paint) {
        // Note: Inner masked paints are handled in GraphicsUtil.safelySetPaint
        this.paint = paint;
    }

    @Override
    public @NotNull Paint paint() {
        return paint;
    }

    @Override
    public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds,
            AffineTransform xform, RenderingHints hints) {
        PaintContext parentContext = paint.createContext(null, deviceBounds, userBounds, xform, hints);
        return new MaskPaintContext(parentContext, maskRaster, maskOffset);
    }

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

    private static final class MaskPaintContext implements PaintContext {
        private final @NotNull PaintContext parentContext;
        private final @NotNull ColorModel colorModel;
        private final int numColorComponents;
        private final @NotNull ColorModel parentColorModel;
        private final @NotNull Raster maskRaster;
        private final @NotNull Point offset;

        MaskPaintContext(@NotNull PaintContext parentContext, @NotNull Raster maskRaster,
                @NotNull Point offset) {
            this.parentContext = parentContext;
            parentColorModel = parentContext.getColorModel();
            this.maskRaster = maskRaster;
            this.offset = offset;
            if (parentContext.getColorModel().hasAlpha()) {
                colorModel = parentColorModel;
            } else {
                colorModel = new ComponentColorModel(parentContext.getColorModel()
                        .getColorSpace(), true, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
            }
            numColorComponents = colorModel.getNumColorComponents();
        }

        @Override
        public @NotNull ColorModel getColorModel() {
            return colorModel;
        }

        @Override
        public void dispose() {
            parentContext.dispose();
        }

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

            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);
                    Graphics graphics = resultImage.getGraphics();
                    graphics.drawImage(parentImage, 0, 0, null);
                    graphics.dispose();
                }
            } 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 = maskRaster.getMinX();
            int softMaskMinY = maskRaster.getMinY();
            int softMaskMaxX = softMaskMinX + maskRaster.getWidth();
            int softMaskMaxY = softMaskMinY + maskRaster.getHeight();

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

                    int luminance;
                    if ((rx >= softMaskMinX) && (rx < softMaskMaxX) && (ry >= softMaskMinY) && (ry < softMaskMaxY)) {
                        luminance = maskRaster.getSample(rx, ry, 0);
                    } else {
                        luminance = 0;
                    }
                    int newAlpha = div255(luminance * result.getSample(i, j, numColorComponents));
                    result.setSample(i, j, numColorComponents, newAlpha);
                }
            }

            return result;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy