com.lizardtech.djvu.GBitmap.java Source code

Java tutorial

Introduction

Here is the source code for com.lizardtech.djvu.GBitmap.java

Source

//C- -------------------------------------------------------------------
//C- Java DjVu (r) (v. 0.8)
//C- Copyright (c) 2004-2005 LizardTech, Inc.  All Rights Reserved.
//C- Java DjVu is protected by U.S. Pat. No.C- 6,058,214 and patents
//C- pending.
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- The computer code originally released by LizardTech under this
//C- license and unmodified by other parties is deemed "the LIZARDTECH
//C- ORIGINAL CODE."  Subject to any third party intellectual property
//C- claims, LizardTech grants recipient a worldwide, royalty-free,
//C- non-exclusive license to make, use, sell, or otherwise dispose of
//C- the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- General Public License.   This grant only confers the right to
//C- infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- the extent such infringement is reasonably necessary to enable
//C- recipient to make, have made, practice, sell, or otherwise dispose
//C- of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- any greater extent that may be necessary to utilize further
//C- modifications or combinations.
//C-
//C- The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C-
//C- In addition, as a special exception, LizardTech Inc. gives permission
//C- to link the code of this program with the proprietary Java
//C- implementation provided by Sun (or other vendors as well), and
//C- distribute linked combinations including the two. You must obey the
//C- GNU General Public License in all respects for all of the code used
//C- other than the proprietary Java implementation. If you modify this
//C- file, you may extend this exception to your version of the file, but
//C- you are not obligated to do so. If you do not wish to do so, delete
//C- this exception statement from your version.
//C- -------------------------------------------------------------------
//C- Developed by Bill C. Riemers, Foxtrot Technologies Inc. as work for
//C- hire under US copyright laws.
//C- -------------------------------------------------------------------
//
package com.lizardtech.djvu;

import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.typedarrays.shared.Uint8Array;

/**
 * This class represents bitonal and gray scale pixel images.
 *
 * @author Bill C. Riemers
 * @version $Revision: 1.15 $
 */
public class GBitmap extends GMap {

    //~ Instance fields --------------------------------------------------------

    private static final int PIXEL_OFFSET = 3; // alpha offset in image data

    /** Color depth */
    protected int grays = 0;

    /** number of border pixels */
    private int border = 0;

    /** number of bytes in each row */
    private int rowSize = 0;

    /** end of the buffer  */
    private int maxRowOffset = 0;

    //~ Constructors -----------------------------------------------------------

    /**
     * Creates a new GBitmap object.
     */
    public GBitmap() {
        super(BYTES_PER_PIXEL, PIXEL_OFFSET, PIXEL_OFFSET, PIXEL_OFFSET, true);
    }

    //~ Methods ----------------------------------------------------------------

    @Override
    public void putData(Context2d target) {
        target.putImageData(imageData, -border, 0);
    }

    /**
     * Query a pixel as boolean
     *
     * @param offset position to query
     *
     * @return true if zero
     */
    public final boolean getBooleanAt(int offset) {
        return (offset < border) || (offset >= maxRowOffset) || (getByteAt(offset) == 0);
    }

    /**
     * Set the pixel value.
     *
     * @param offset position of the pixel to set
     * @param value gray scale value to set
     */
    public final void setByteAt(int offset, int value) {
        if ((offset >= border) || (offset < maxRowOffset)) {
            data.set(offset * ncolors + PIXEL_OFFSET, 255 * value / (grays - 1));
        }
    }

    /**
     * Query the pixel at a particular location
     *
     * @param offset the pixel location
     *
     * @return the gray scale value
     */
    public final int getByteAt(final int offset) {
        int value = offset >= 0 ? data.get(offset * ncolors + PIXEL_OFFSET) : 0;
        return ((value * (grays - 1) + (grays - 2))) / 255;
    }

    /**
     * Query the color depth.
     *
     * @return number of grays
     */
    public final int getGrays() {
        return grays;
    }

    /**
     * Set the color depth
     *
     * @param ngrays number of grays
     *
     * @throws IllegalArgumentException if the number of grays is less than 2 or greater than 256
     */
    public void setGrays(int ngrays) {
        if ((ngrays < 2) || (ngrays > 256)) {
            throw new IllegalArgumentException("(GBitmap::set_grays) Illegal number of gray levels");
        }

        grays = ngrays;
    }

    /**
     * Set the number of bytes per rows.
     *
     * @param rowSize the number of bytes per row
     */
    private final void setRowSize(final int rowSize) {
        this.rowSize = rowSize;
        maxRowOffset = rowOffset(nrows);
    }

    /**
     * Set the number of rows.
     *
     * @param rows number of rows
     */
    private final void setRows(final int rows) {
        if (rows != nrows) {
            nrows = rows;
            maxRowOffset = rowOffset(nrows);
        }
    }

    /**
     * Insert another bitmap at the specified location.  Note that both bitmaps
     * need to have the same number of grays.
     *
     * @param bm bitmap to insert
     * @param xh horizontal location to insert at
     * @param yh vertical location to insert at
     * @param subsample rate to subsample at
     *
     * @return true if the blit intersected this bitmap
     */
    public boolean blit(final GBitmap bm, final int xh, final int yh, final int subsample) {
        int pidx = 0;
        int qidx = 0;

        if (subsample == 1) {
            return insertMap(bm, xh, yh, true);
        }

        if ((xh >= (ncolumns * subsample)) || (yh >= (nrows * subsample)) || ((xh + bm.columns()) < 0)
                || ((yh + bm.rows()) < 0)) {
            return false;
        }

        if (bm.data != null) {
            int dr = yh / subsample;
            int dr1 = yh - (subsample * dr);

            if (dr1 < 0) {
                dr--;
                dr1 += subsample;
            }

            int zdc = xh / subsample;
            int zdc1 = xh - (subsample * zdc);

            if (zdc1 < 0) {
                zdc--;
                zdc1 += subsample;
            }

            int sr = 0;

            for (; sr < bm.rows(); sr++) {
                if ((dr >= 0) && (dr < nrows)) {
                    int dc = zdc;
                    int dc1 = zdc1;
                    qidx = bm.rowOffset(sr);
                    pidx = rowOffset(dr);

                    for (int sc = 0; sc < bm.columns(); sc++) {
                        if ((dc >= 0) && (dc < ncolumns)) {
                            int i = pidx + dc;
                            setByteAt(i, getByteAt(i) + bm.getByteAt(qidx + sc));
                        }

                        if (++dc1 >= subsample) {
                            dc1 = 0;
                            dc++;
                        }
                    }
                }

                if (++dr1 >= subsample) {
                    dr1 = 0;
                    dr++;
                }
            }
        }

        return true;
    }

    /**
     * Query the start offset of a row.
     *
     * @param row the row to query
     *
     * @return the offset to the pixel data
     */
    @Override
    public final int rowOffset(final int row) {
        return (row * rowSize) + border;
    }

    /**
     * Query the number of bytes per row.
     *
     * @return bytes per row
     */
    @Override
    public final int getRowSize() {
        return rowSize;
    }

    /**
     * Insert the reference map at the specified location.
     *
     * @param bit map to insert
     * @param dx horizontal position to insert at
     * @param dy vertical position to insert at
     * @param doBlit true if the gray scale values should be added
     *
     * @return true if pixels are inserted
     */
    private boolean insertMap(final GBitmap bit, final int dx, final int dy, final boolean doBlit) {
        final int x0 = Math.max(dx, 0);
        int y0 = Math.max(dy, 0);
        final int x1 = Math.max(-dx, 0);
        int y1 = Math.max(-dy, 0);
        final int w = Math.min(columns() - x0, bit.columns() - x1);
        int h = Math.min(rows() - y0, bit.rows() - y1);

        if ((w > 0) && (h > 0)) {
            final int gmax = (byte) (grays - 1);
            do {
                int offset = rowOffset(y0++) + x0;
                int refOffset = bit.rowOffset(y1++) + x1;

                if (doBlit) {
                    // This is not really correct.  We should reduce the original level by the
                    // amount of the new level.  But since we are normally dealing with non-overlapping
                    // or bitonal blits it really doesn't matter.
                    int i = w;
                    do {
                        final int g = getByteAt(offset) + bit.getByteAt(refOffset++);
                        setByteAt(offset++, (byte) Math.min(g, gmax));
                    } while (--i > 0);
                } else {
                    data.set(bit.data.subarray(refOffset * ncolors, (refOffset + w) * ncolors), offset * ncolors);
                }
            } while (--h > 0);
            return true;
        }
        return false;
    }

    /**
     * Initialize this image with the specified values.
     *
     * @param arows number of rows
     * @param acolumns number of columns
     * @param aborder width of the border
     *
     * @return the initialized image map
     */
    public GBitmap init(final int arows, final int acolumns, final int aborder) {
        data = null;
        grays = 2;
        setRows(arows);
        ncolumns = acolumns;
        border = aborder;
        setRowSize(ncolumns + border);

        int npixels = rowOffset(nrows) * ncolors;

        if (npixels > 0) {
            setImageData(imageDataFactory.createImageData(rowSize, nrows + 3));
        }

        return this;
    }

    /**
     * Initialize this map by copying a reference map
     *
     * @param ref map to copy
     * @param aborder number of border pixels
     *
     * @return the initialized map
     */
    private GBitmap init(final GBitmap ref, final int aborder) {
        if (this != ref) {
            init(ref.rows(), ref.columns(), aborder);
            grays = ref.grays;

            for (int i = 0; i < nrows; i++) {
                Uint8Array refRow = ref.data.subarray(ref.rowOffset(i) * ncolors,
                        ref.rowOffset(i) + ncolumns * ncolors);
                data.set(refRow, rowOffset(i) * ncolors);
            }
        } else if (aborder > border) {
            setMinimumBorder(aborder);
        }

        return this;
    }

    /**
     * Set the minimum border needed
     *
     * @param minimum the minumum border needed
     */
    public void setMinimumBorder(final int minimum) {
        if (border < minimum) {
            if (data != null) {
                GBitmap tmp = new GBitmap().init(this, minimum);
                setRowSize(tmp.getRowSize());
                data = tmp.data;
                tmp.data = null;

                if (DjVuOptions.COLLECT_GARBAGE) {
                    System.gc();
                }
            }

            setBorder(minimum);
        }
    }

    /**
     * Set the border width.
     *
     * @param border border width
     */
    private void setBorder(final int border) {
        this.border = border;
        maxRowOffset = rowOffset(nrows);
    }

    /**
     * Query if we are allowed to skip the ramp call.
     *
     * @return true if not 24 bit color
     */
    @Override
    public boolean isRampNeeded() {
        return true;
    }

    /**
     * Find the bounding box for non-white pixels.
     *
     * @return bounding rectangle
     */
    public synchronized GRect compute_bounding_box() {
        final int w = columns();
        final int h = rows();
        final int s = getRowSize();

        int xmin, xmax, ymin, ymax;
        for (xmax = w - 1; xmax >= 0; xmax--) {
            int p = rowOffset(0) + xmax;
            final int pe = p + (s * h);

            while ((p < pe) && getBooleanAt(p)) {
                p += s;
            }

            if (p < pe) {
                break;
            }
        }

        for (ymax = h - 1; ymax >= 0; ymax--) {
            int p = rowOffset(ymax);
            final int pe = p + w;

            while ((p < pe) && getBooleanAt(p)) {
                ++p;
            }

            if (p < pe) {
                break;
            }
        }

        for (xmin = 0; xmin <= xmax; xmin++) {
            int p = rowOffset(0) + xmin;
            final int pe = p + (s * h);

            while ((p < pe) && getBooleanAt(p)) {
                p += s;
            }

            if (p < pe) {
                break;
            }
        }

        for (ymin = 0; ymin <= ymax; ymin++) {
            int p = rowOffset(ymin);
            final int pe = p + w;

            while ((p < pe) && getBooleanAt(p)) {
                ++p;
            }

            if (p < pe) {
                break;
            }
        }
        final GRect retval = new GRect();
        retval.xmin = xmin;
        retval.xmax = xmax;
        retval.ymin = ymin;
        retval.ymax = ymax;
        return retval;
    }
}