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

org.jpedal.parser.image.downsample.OneBitDownSampler Maven / Gradle / Ivy

There is a newer version: 7.15.25
Show newest version
/*
 * ===========================================
 * 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
 *
 @LICENSE@
 *
 * ---------------
 * OneBitDownSampler.java
 * ---------------
 */
package org.jpedal.parser.image.downsample;

import org.jpedal.color.ColorSpaces;
import org.jpedal.color.DeviceRGBColorSpace;
import org.jpedal.color.GenericColorSpace;
import org.jpedal.images.SamplingFactory;
import org.jpedal.parser.image.PdfImageTypes;
import org.jpedal.parser.image.data.ImageData;

/**
 * @author markee
 */
class OneBitDownSampler {

    public static GenericColorSpace downSample(final int sampling, final ImageData imageData, GenericColorSpace decodeColorData) {

        final byte[] data = imageData.getObjectData();

        final int[] flag = {1, 2, 4, 8, 16, 32, 64, 128};

        /* if(sampling>2){
            
            System.out.println("downSample ="+sampling);
        
            int newW=imageData.getWidth()>>1;
            int newH=imageData.getHeight()>>1;
        
            final int origLineLength= (imageData.getWidth()+7)>>3;
            final int newLineLength= (newW+7)>>3;

            int size=(newLineLength*newH);
        
            byte[] newData=new byte[size];
         
            //scan all pixels and down-sample
            for(int y=0;ywGapLeft) {
                        wCount = wGapLeft;
                    }
                    if(hCount>hGapLeft) {
                        hCount = hGapLeft;
                    }

                    //count pixels in sample we will make into a pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels)
                    final int bytes = getPixelSetCount(2, false, data, flag, origLineLength, y, x, wCount, hCount);

                    final int inputByte=(x>>2)+(origLineLength*(y<<2));
                    final int outputByte=(x>>3)+(newLineLength*y);

                    if(bytes>3){
                        //set value as white or average of pixels
                     //   final int outputByte=((x>>1) )+(newLineLength*(y>>2));
                       
                        newData[outputByte] = (byte) (newData[outputByte] | (flag[7-((x & 7))]));
                       
                    }
                }
            }
            
            imageData.setWidth(newW);
            imageData.setHeight(newH);
            data=newData;
            
            sampling=sampling>>1;
        }
        
        System.out.println("downSample ="+sampling);
        /**/
        final int newW = imageData.getWidth() / sampling;
        final int newH = imageData.getHeight() / sampling;

        final int size = newW * newH;

        final byte[] newData = new byte[size];

        final int origLineLength = (imageData.getWidth() + 7) >> 3;

        //scan all pixels and down-sample
        for (int y = 0; y < newH; y++) {
            for (int x = 0; x < newW; x++) {

                //allow for edges in number of pixels left
                int wCount = sampling, hCount = sampling;
                final int wGapLeft = imageData.getWidth() - x;
                final int hGapLeft = imageData.getHeight() - y;
                if (wCount > wGapLeft) {
                    wCount = wGapLeft;
                }
                if (hCount > hGapLeft) {
                    hCount = hGapLeft;
                }

                //count pixels in sample we will make into a pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels)
                final int bytes = getPixelSetCount(sampling, false, data, flag, origLineLength, y, x, wCount, hCount);

                final int count = (wCount * hCount);

                //set value as white or average of pixels
                final int offset = x + (newW * y);

                if (count > 0) {
                    newData[offset] = (byte) ((255 * bytes) / count);
                } else {
                    newData[offset] = (byte) 255;
                }
            }
        }

        imageData.setWidth(newW);
        imageData.setHeight(newH);
        imageData.setCompCount(1);

        //suggest you add kernel sharpening here
        //@bethan
        //imageData=KernelUtils.sharpenGrayScale(imageData,w,h);
        //remap Separation as already converted here
        if (decodeColorData.getID() == ColorSpaces.Separation || decodeColorData.getID() == ColorSpaces.DeviceN) {
            decodeColorData = new DeviceRGBColorSpace();

            imageData.setCompCount(1);
            invertBytes(newData);
        }

        imageData.setObjectData(newData);

        imageData.setDepth(8);

        return decodeColorData;
    }

    private static void invertBytes(final byte[] newData) {
        final int count = newData.length;
        for (int aa = 0; aa < count; aa++) {
            newData[aa] = (byte) (newData[aa] ^ 255);
        }
    }

    public static void downsampleTo8Bit(final int sampling, final ImageData imageData, final boolean isMask) {

        final byte[] data = imageData.getObjectData();

        final int newW = imageData.getWidth() / sampling;
        final int newH = imageData.getHeight() / sampling;

        final int size = newW * newH;

        final byte[] newData = new byte[size];

        final int[] flag = {1, 2, 4, 8, 16, 32, 64, 128};

        final int origLineLength = (imageData.getWidth() + 7) >> 3;

        //scan all pixels and down-sample
        for (int y = 0; y < newH; y++) {
            for (int x = 0; x < newW; x++) {

                //allow for edges in number of pixels left
                int wCount = sampling, hCount = sampling;
                final int wGapLeft = imageData.getWidth() - x;
                final int hGapLeft = imageData.getHeight() - y;
                if (wCount > wGapLeft) {
                    wCount = wGapLeft;
                }
                if (hCount > hGapLeft) {
                    hCount = hGapLeft;
                }

                //count pixels in sample we will make into a pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels)
                final int bytes = getPixelSetCount(sampling, isMask, data, flag, origLineLength, y, x, wCount, hCount);

                final int count = (wCount * hCount);

                //set value as white or average of pixels
                final int offset = x + (newW * y);

                if (bytes > 0) {
                    newData[offset] = (byte) (255 * bytes / count);
                }
            }
        }

        imageData.setWidth(newW);
        imageData.setHeight(newH);
        imageData.setObjectData(newData);

        imageData.setDepth(8);
        imageData.setCompCount(1);

    }

    static void convertToARGB(final ImageData imageData, final byte[] maskCol, final boolean scaleTransparency) {

        final byte[] data = imageData.getObjectData();

        final int newW = imageData.getWidth();
        final int newH = imageData.getHeight();

        final int size = newW * newH * 4;

        final byte[] newData = new byte[size];

        //scan all pixels and down-sample
        for (int y = 0; y < newH; y++) {
            for (int x = 0; x < newW; x++) {

                //set value as white or average of pixels
                final int offset = x + (newW * y);

                final int bytes = data[offset] & 255;

                int ptr = 4;

                if (bytes < 128) {
                    ptr = 0;
                }

                System.arraycopy(maskCol, 0 + ptr, newData, (offset * 4) + 0, 3);
                if (scaleTransparency) {
                    newData[(offset * 4) + 3] = (byte) (((maskCol[3 + ptr] & 255) * bytes) >> 8);
                } else {
                    newData[(offset * 4) + 3] = maskCol[3 + ptr];
                }
            }
        }

        imageData.setWidth(newW);
        imageData.setHeight(newH);

        imageData.setObjectData(newData);

        imageData.setDepth(8);

    }

    private static int getPixelSetCount(final int sampling, final boolean imageMask, final byte[] data, final int[] flag, final int origLineLength,
            final int y, final int x, final int wCount, final int hCount) {

        byte currentByte;
        int bit;
        int bytes = 0;
        int ptr;

        for (int yy = 0; yy < hCount; yy++) {
            for (int xx = 0; xx < wCount; xx++) {

                ptr = ((yy + (y * sampling)) * origLineLength) + (((x * sampling) + xx) >> 3);

                if (ptr < data.length) {
                    currentByte = data[ptr];
                } else {
                    currentByte = 0;
                }

                if (imageMask) {
                    currentByte = (byte) (currentByte ^ 255);
                }

                bit = currentByte & flag[7 - (((x * sampling) + xx) & 7)];

                if (bit != 0) {
                    bytes++;
                }
            }
        }
        return bytes;
    }

    public static GenericColorSpace downSample(GenericColorSpace decodeColorData, final byte[] maskCol, final int sampling, final ImageData imageData) {

        byte[] index = decodeColorData.getIndexedMap();

        final boolean needsSharpening = (SamplingFactory.kernelSharpen
                || SamplingFactory.downsampleLevel == SamplingFactory.mediumAndSharpen
                || SamplingFactory.downsampleLevel == SamplingFactory.highAndSharpen);

        byte[] colIndex = null;

        boolean scaleTransparency = true;
        boolean isMask = false;

        if (index != null) {
            index = decodeColorData.convertIndexToRGB(index);

            colIndex = new byte[]{index[0], index[1], index[2], (byte) 255, index[3], index[4], index[5], (byte) 255};

            decodeColorData = new DeviceRGBColorSpace();

            scaleTransparency = false;

        } else if (maskCol != null) {
            isMask = true;
            colIndex = new byte[]{0, 0, 0, (byte) 255, maskCol[0], maskCol[1], maskCol[2], (byte) 255};
        }

        if (colIndex != null) {

            OneBitDownSampler.downsampleTo8Bit(sampling, imageData, isMask);
            if (needsSharpening && sampling < 8) {
                KernelUtils.applyKernel(imageData, 1);
            }

            OneBitDownSampler.convertToARGB(imageData, colIndex, scaleTransparency);
            imageData.setIsConvertedToARGB(true);

        } else {
            decodeColorData = OneBitDownSampler.downSample(sampling, imageData, decodeColorData);
            imageData.setImageType(PdfImageTypes.Binary);

            if (needsSharpening && sampling < 8) {
                KernelUtils.applyKernel(imageData, 1);
            }
        }
        return decodeColorData;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy