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

com.idrsolutions.pdf.color.shading.LatticeFormShadeContext Maven / Gradle / Ivy

/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/support/
 *
 * (C) Copyright 1997-2017 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * LatticeFormShadeContext.java
 * ---------------
 */
package com.idrsolutions.pdf.color.shading;

import java.awt.Color;
import java.awt.PaintContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.List;

import org.jpedal.color.GenericColorSpace;
import org.jpedal.function.PDFFunction;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.utils.Matrix;

/**
 * @author suda
 */
public class LatticeFormShadeContext implements PaintContext {

    private final GenericColorSpace shadingColorSpace;

    private final float[] background;
    //final double textX;
    //final double textY;

    private final float[][] toUserSpace;
    private final float[][] toShadeSpace;
    private final int bitsPerComponent;
    private final int bitsPerFlag;
    private final int bitsPerCoordinate;
    private final float[] decodeArr;
    private final PDFFunction[] function;
    private final int nComp;

    private final List triCoords;
    private final List triColors;
    private final int triCount;
    private final int verticesPerRow;

    public LatticeFormShadeContext(final AffineTransform xform, final GenericColorSpace shadingColorSpace, final float[] background, final PdfObject shadingObject, final float[][] sm, final PDFFunction[] function) {

        this.shadingColorSpace = shadingColorSpace;

        this.bitsPerComponent = shadingObject.getInt(PdfDictionary.BitsPerComponent);
        this.bitsPerFlag = shadingObject.getInt(PdfDictionary.BitsPerFlag);
        this.bitsPerCoordinate = shadingObject.getInt(PdfDictionary.BitsPerCoordinate);
        this.decodeArr = shadingObject.getFloatArray(PdfDictionary.Decode);
        this.function = function;
        this.background = background;
        this.nComp = (this.decodeArr.length - 4) / 2;
        this.verticesPerRow = shadingObject.getInt(PdfDictionary.VerticesPerRow);

        float[][] shadeMatrix = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
        if (sm != null) {
            shadeMatrix = sm;
        }

        final float[][] xformMatrix = {
                {(float) xform.getScaleX(), (float) xform.getShearX(), 0},
                {(float) xform.getShearY(), (float) xform.getScaleY(), 0},
                {(float) xform.getTranslateX(), (float) xform.getTranslateY(), 1}
        };
        toUserSpace = Matrix.inverse(xformMatrix);
        toShadeSpace = Matrix.inverse(shadeMatrix);

        final boolean hasSmallBits = bitsPerFlag < 8 || bitsPerComponent < 8 || bitsPerCoordinate < 8;
        final BitReader reader = new BitReader(shadingObject.getDecodedStream(), hasSmallBits);

        final double bitCoordScaling = 1.0 / ((1L << bitsPerCoordinate) - 1);
        final double bitCompScaling = 1.0 / ((1L << bitsPerComponent) - 1);

        final List trianglesPoints = new ArrayList();
        triColors = new ArrayList();

        final ArrayList pList = new ArrayList();
        final ArrayList cList = new ArrayList();
        while (reader.getPointer() < reader.getTotalBitLen()) {
            final Point2D p = getPointCoords(reader, bitCoordScaling, bitsPerCoordinate, decodeArr);
            final Color c = getPointColor(reader, bitCompScaling, bitsPerComponent);
            pList.add(p);
            cList.add(c);
        }
        final int totalRows = pList.size() / verticesPerRow;

        //populate triangles : please refer to the pdf spec for understanding
        for (int mm = 0; mm < (totalRows - 1); mm++) {
            final int mRows = mm * totalRows;
            for (int nn = 0; nn < (verticesPerRow - 1); nn++) {
                final int nm = nn + mRows;
                final int[] t = new int[6];
                t[0] = nm;
                t[1] = t[3] = nm + 1;
                t[2] = t[4] = nm + verticesPerRow;
                t[5] = nm + verticesPerRow + 1;
                for (int z = 0; z < t.length; z++) {
                    trianglesPoints.add(pList.get(t[z]));
                    triColors.add(cList.get(t[z]));
                }
            }
        }

        triCoords = new ArrayList();
        for (final Point2D trianglesPoint : trianglesPoints) {
            final float[] xy = new float[2];
            xy[0] = (float) trianglesPoint.getX();
            xy[1] = (float) trianglesPoint.getY();
            triCoords.add(xy);
        }
        triCount = triCoords.size() / 3;
        Matrix.show(shadeMatrix);
    }

    @Override
    public void dispose() {

    }

    @Override
    public ColorModel getColorModel() {
        return ColorModel.getRGBdefault();
    }

    private static Point2D getPointCoords(final BitReader reader, final double bitScaling, final int bps, final float[] decode) {
        final long x_ = reader.readBitsAsLong(bps);
        final long y_ = reader.readBitsAsLong(bps);

        final double x = x_ * bitScaling * (decode[1] - decode[0]) + decode[0];
        final double y = y_ * bitScaling * (decode[3] - decode[2]) + decode[2];
        return new Point2D.Double(x, y);
    }

    private Color getPointColor(final BitReader reader, final double bitScaling, final int bps) {
        final float[] components = new float[nComp];
        for (int i = 0, j = 4; i < nComp; i++, j += 2) {
            final long ci = reader.readBitsAsLong(bps);
            components[i] = (float) (ci * bitScaling * (decodeArr[j + 1] - decodeArr[j]) + decodeArr[j]);
        }
        return calculateColor(components);
    }

    @Override
    public Raster getRaster(final int startX, final int startY, final int w, final int h) {

        final int rastSize = (w * h * 4);
        final int[] data = new int[rastSize];

        if (background != null) {
            shadingColorSpace.setColor(background, 4);
            final Color c = (Color) shadingColorSpace.getColor();
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    final int base = (i * w + j) * 4;
                    data[base] = c.getRed();
                    data[base + 1] = c.getGreen();
                    data[base + 2] = c.getBlue();
                    data[base + 3] = 255;
                }
            }
        }

        float x, y, x1, y1, x2, y2, x3, y3;
        float[] temp;
        int r, g, b;

        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                float[] src = {startX + j, startY + i};
                src = Matrix.transformPoint(toUserSpace, src[0], src[1]);
                src = Matrix.transformPoint(toShadeSpace, src[0], src[1]);

                x = src[0];
                y = src[1];

                for (int t = 0; t < triCount; t++) {
                    final int p = t * 3;

                    temp = triCoords.get(p);
                    x1 = temp[0];
                    y1 = temp[1];

                    temp = triCoords.get(p + 1);
                    x2 = temp[0];
                    y2 = temp[1];

                    temp = triCoords.get(p + 2);
                    x3 = temp[0];
                    y3 = temp[1];

                    if (isInTriangle(x, y, x1, y1, x2, y2, x3, y3)) { //shapes.get(t).contains(x, y)) { //

                        final Color c1 = triColors.get(p);
                        final Color c2 = triColors.get(p + 1);
                        final Color c3 = triColors.get(p + 2);

                        final float a = areaTriangle(x1, y1, x2, y2, x3, y3);
                        final float a1 = areaTriangle(x, y, x1, y1, x2, y2);
                        final float a2 = areaTriangle(x, y, x1, y1, x3, y3);
                        final float a3 = areaTriangle(x, y, x2, y2, x3, y3);

                        r = (int) ((a1 / a) * c3.getRed() + (a2 / a) * c2.getRed() + (a3 / a) * c1.getRed());
                        g = (int) ((a1 / a) * c3.getGreen() + (a2 / a) * c2.getGreen() + (a3 / a) * c1.getGreen());
                        b = (int) ((a1 / a) * c3.getBlue() + (a2 / a) * c2.getBlue() + (a3 / a) * c1.getBlue());

                        final int base = (i * w + j) * 4;
                        data[base] = r;
                        data[base + 1] = g;
                        data[base + 2] = b;
                        data[base + 3] = 255;
                        break;
                    }
                }
            }
        }

        final WritableRaster raster = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB).getRaster();
        raster.setPixels(0, 0, w, h, data);
        return raster;

    }

    private Color calculateColor(final float[] val) {
        final Color col;
        if (function != null) {
            final float[] colValues = ShadingFactory.applyFunctions(function, val);
            shadingColorSpace.setColor(colValues, colValues.length);
            col = (Color) shadingColorSpace.getColor();
        } else {
            shadingColorSpace.setColor(val, val.length);
            col = (Color) shadingColorSpace.getColor();
        }
        return col;
    }

    public float areaTriangle(final float x1, final float y1, final float x2, final float y2, final float x3, final float y3) {
        return Math.abs((x1 - x3) * (y2 - y1) - (x1 - x2) * (y3 - y1));
    }

    private static boolean isInTriangle(final float x, final float y, final float x1, final float y1, final float x2, final float y2, final float x3, final float y3) {
        final float dX = x - x3;
        final float dY = y - y3;
        final float dX21 = x3 - x2;
        final float dY12 = y2 - y3;
        final float D = dY12 * (x1 - x3) + dX21 * (y1 - y3);
        final float s = dY12 * dX + dX21 * dY;
        final float t = (y3 - y1) * dX + (x1 - x3) * dY;
        if (D < 0) {
            return s <= 0 && t <= 0 && s + t >= D;
        }
        return s >= 0 && t >= 0 && s + t <= D;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy