package org.ebookdroid.core; import org.ebookdroid.common.bitmaps.ByteBufferBitmap; import org.ebookdroid.common.bitmaps.ByteBufferManager; import org.ebookdroid.common.bitmaps.GLBitmaps; import org.ebookdroid.common.settings.books.BookSettings; import org.ebookdroid.common.settings.types.DocumentViewMode; import org.ebookdroid.common.settings.types.PageType; import org.ebookdroid.core.codec.CodecPageInfo; import org.ebookdroid.core.codec.PageLink; import org.ebookdroid.core.crop.PageCropper; import org.ebookdroid.ui.viewer.IActivityController; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.RectF; import android.util.FloatMath; import java.util.List; import org.emdev.common.log.LogContext; import org.emdev.common.log.LogManager; import org.emdev.utils.MathUtils; import org.emdev.utils.MatrixUtils; public class Page { static final LogContext LCTX = LogManager.root().lctx("Page", false); public final PageIndex index; public final PageType type; public final CodecPageInfo cpi; final IActivityController base; public final PageTree nodes; RectF bounds; int aspectRatio; boolean recycled; float storedZoom; RectF zoomedBounds; int zoomLevel = 1; List<PageLink> links; public Page(final IActivityController base, final PageIndex index, final PageType pt, final CodecPageInfo cpi) { this.base = base; this.index = index; this.cpi = cpi; this.type = pt != null ? pt : PageType.FULL_PAGE; this.bounds = new RectF(0, 0, cpi.width / type.getWidthScale(), cpi.height); setAspectRatio(cpi); nodes = new PageTree(this); } public void recycle(final List<GLBitmaps> bitmapsToRecycle) { recycled = true; nodes.recycleAll(bitmapsToRecycle, true); } public float getAspectRatio() { return aspectRatio / 128.0f; } private boolean setAspectRatio(final float aspectRatio) { final int newAspectRatio = (int) Math.floor(aspectRatio * 128); if (this.aspectRatio != newAspectRatio) { this.aspectRatio = newAspectRatio; return true; } return false; } public boolean setAspectRatio(final CodecPageInfo page) { if (page != null) { return this.setAspectRatio(page.width / type.getWidthScale(), page.height); } return false; } public boolean setAspectRatio(final float width, final float height) { return setAspectRatio(width / height); } public void setBounds(final RectF pageBounds) { storedZoom = 0.0f; zoomedBounds = null; bounds = pageBounds; } public void setBounds(final float l, final float t, final float r, final float b) { if (bounds == null) { bounds = new RectF(l, t, r, b); } else { bounds.set(l, t, r, b); } } public boolean shouldCrop() { final BookSettings bs = base.getBookSettings(); if (nodes.root.hasManualCropping()) { return true; } return bs != null && bs.cropPages; } public RectF getCropping() { return shouldCrop() ? nodes.root.getCropping() : null; } public RectF getCropping(PageTreeNode node) { return shouldCrop() ? node.getCropping() : null; } protected void updateAspectRatio() { final RectF cropping = getCropping(); if (cropping != null) { final float pageWidth = cpi.width * cropping.width(); final float pageHeight = cpi.height * cropping.height(); setAspectRatio(pageWidth, pageHeight); } else { setAspectRatio(cpi); } } public RectF getBounds(final float zoom) { RectF bounds = new RectF(); getBounds(zoom, bounds); return bounds; } public void getBounds(final float zoom, RectF target) { MathUtils.zoom(bounds, zoom, target); // If the unzoomed page fit on screen with room to spare, re-center the zoomed page // TODO: clarify these calculations final BookSettings bs = base.getBookSettings(); if (bs != null && bs.viewMode == DocumentViewMode.SINGLE_PAGE) { if (bounds.left > 0) { target.offset((bounds.left + bounds.right) * (1 - zoom) / 2, 0); } if (bounds.top > 0) { target.offset(0, (bounds.top + bounds.bottom) * (1 - zoom) / 2); } } } public float getTargetRectScale() { return type.getWidthScale(); } @Override public String toString() { final StringBuilder buf = new StringBuilder("Page"); buf.append("["); buf.append("index").append("=").append(index); buf.append(", "); buf.append("bounds").append("=").append(bounds); buf.append(", "); buf.append("aspectRatio").append("=").append(aspectRatio); buf.append(", "); buf.append("type").append("=").append(type.name()); buf.append("]"); return buf.toString(); } public static RectF getTargetRect(final PageType pageType, final RectF pageBounds, final RectF normalizedRect) { final Matrix tmpMatrix = MatrixUtils.get(); tmpMatrix.postScale(pageBounds.width() * pageType.getWidthScale(), pageBounds.height()); tmpMatrix.postTranslate(pageBounds.left - pageBounds.width() * pageType.getLeftPos(), pageBounds.top); final RectF targetRectF = new RectF(); tmpMatrix.mapRect(targetRectF, normalizedRect); MathUtils.floor(targetRectF); return targetRectF; } public RectF getLinkSourceRect(final RectF pageBounds, final PageLink link) { if (link == null || link.sourceRect == null) { return null; } return getPageRegion(pageBounds, new RectF(link.sourceRect)); } public RectF getPageRegion(final RectF pageBounds, final RectF sourceRect) { final RectF cb = getCropping(); if (cb != null) { final Matrix m = MatrixUtils.get(); final RectF psb = nodes.root.pageSliceBounds; m.postTranslate(psb.left - cb.left, psb.top - cb.top); m.postScale(psb.width() / cb.width(), psb.height() / cb.height()); m.mapRect(sourceRect); } if (type == PageType.LEFT_PAGE && sourceRect.left >= 0.5f) { return null; } if (type == PageType.RIGHT_PAGE && sourceRect.right < 0.5f) { return null; } return getTargetRect(type, pageBounds, sourceRect); } protected RectF getColumn(final PointF pos) { final DecodeService ds = base.getDecodeService(); final ByteBufferBitmap pageImage = ds.createPageThumbnail(PageCropper.BMP_SIZE, PageCropper.BMP_SIZE, index.docIndex, type.getInitialRect()); final RectF column = PageCropper.getColumn(pageImage, pos.x, pos.y); ByteBufferManager.release(pageImage); return column; } }