package com.xenoage.zong.android.scoreview; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import com.xenoage.utils.math.Units; import com.xenoage.utils.math.geom.Point2f; import com.xenoage.utils.math.geom.Size2f; import com.xenoage.utils.math.geom.Size2i; import com.xenoage.zong.android.App; import com.xenoage.zong.android.R; import com.xenoage.zong.android.renderer.canvas.AndroidCanvas; import com.xenoage.zong.core.Score; import com.xenoage.zong.core.format.PageFormat; import com.xenoage.zong.core.format.PageMargins; import com.xenoage.zong.core.format.ScoreFormat; import com.xenoage.zong.core.format.SystemLayout; import com.xenoage.zong.core.header.ColumnHeader; import com.xenoage.zong.core.header.ScoreHeader; import com.xenoage.zong.documents.ScoreDoc; import com.xenoage.zong.layout.Layout; import com.xenoage.zong.layout.Page; import com.xenoage.zong.layout.frames.ScoreFrame; import com.xenoage.zong.layout.frames.ScoreFrameChain; import com.xenoage.zong.musiclayout.ScoreLayout; import com.xenoage.zong.musiclayout.layouter.Context; import com.xenoage.zong.musiclayout.layouter.ScoreLayoutArea; import com.xenoage.zong.musiclayout.layouter.ScoreLayouter; import com.xenoage.zong.musiclayout.layouter.Target; import com.xenoage.zong.renderer.canvas.CanvasDecoration; import com.xenoage.zong.renderer.canvas.CanvasFormat; import com.xenoage.zong.renderer.canvas.CanvasIntegrity; import static com.xenoage.utils.NullUtils.notNull; import static com.xenoage.utils.kernel.Range.range; import static com.xenoage.zong.android.renderer.AndroidLayoutRenderer.androidLayoutRenderer; /** * This class manages the bitmaps of a given {@link ScoreDoc} * and scaling shown on a {@link ScorePageView}. * * When the document or the scaling is changed, a new instance * of this class is required. * * @author Andreas Wenger */ public class ScreenViewBitmaps { //constants private final static int marginPx = 32; //layout private final String title; private final Layout layout; //view private final Size2i screenSize; private final float scaling; private final float titleTextHeightPx; //bitmaps and resources private final Bitmap[] pageBitmaps; private final Resources resources; public ScreenViewBitmaps(ScoreDoc doc, Size2i screenSize, float scaling, Resources res) { this.screenSize = screenSize; this.scaling = scaling; this.resources = res; this.title = notNull(doc.getScore().getInfo().getTitle(), "Untitled Score"); //recompute the layout, so that each page fits into the available screen space int pageWidthPx = screenSize.width; int pageHeightPx = screenSize.height; float pageWidthMm = Units.pxToMm(pageWidthPx, scaling); float pageHeightMm = Units.pxToMm(pageHeightPx, scaling); Size2f pageSizeMm = new Size2f(pageWidthMm, pageHeightMm); float marginMm = Units.pxToMm(marginPx, scaling); PageFormat pageFormat = new PageFormat(pageSizeMm, new PageMargins(marginMm, marginMm, marginMm, marginMm)); Size2f frameSizeMm = new Size2f(pageFormat.getUseableWidth(), pageFormat.getUseableHeight()); //first page needs space for title text titleTextHeightPx = screenSize.height / 20; float firstFrameOffsetY = Units.pxToMm(titleTextHeightPx, scaling); Size2f firstFrameSizeMm = new Size2f(frameSizeMm.width, frameSizeMm.height - firstFrameOffsetY); //delete unnecessary layout information, like system distances //or system breaks Score score = doc.getScore(); score.setFormat(new ScoreFormat()); ScoreHeader header = score.getHeader(); for (int i : range(header.getSystemLayouts())) { SystemLayout sl = header.getSystemLayout(i); if (sl != null) { sl.setDistance(new SystemLayout().getDistance()); } } for (int i : range(header.getColumnHeaders())) { ColumnHeader ch = header.getColumnHeader(i); if (ch.getMeasureBreak() != null) { ch.setBreak(null); } } //layout the score to find out the needed space Context context = new Context(score, App.getSymbolPool(), doc.getLayout().getDefaults().getLayoutSettings()); Target target = Target.completeLayoutTarget(new ScoreLayoutArea(frameSizeMm)); ScoreLayouter layouter = new ScoreLayouter(context, target); ScoreLayout scoreLayout = layouter.createScoreLayout(); //create and fill at least one page Layout layout = new Layout(doc.getLayout().getDefaults()); ScoreFrameChain chain = null; for (int i = 0; i < scoreLayout.frames.size(); i++) { Page page = new Page(pageFormat); Point2f position; Size2f size; if (i == 0) { //first page position = new Point2f(pageSizeMm.width / 2, pageSizeMm.height / 2 + firstFrameOffsetY); size = firstFrameSizeMm; } else { //other pages position = new Point2f(pageSizeMm.width / 2, pageSizeMm.height / 2); size = frameSizeMm; } ScoreFrame frame = new ScoreFrame(); frame.setPosition(position); frame.setSize(size); page.addFrame(frame); layout.addPage(page); if (chain == null) { chain = new ScoreFrameChain(score); chain.setScoreLayout(scoreLayout); } chain.add(frame); } //create a bitmap array with one element for each page pageBitmaps = new Bitmap[layout.getPages().size()]; this.layout = layout; } /** * Gets the number of pages. */ public int getPagesCount() { return layout.getPages().size(); } /** * Gets the bitmap for the page with the given index. * If not loaded, it is created. */ public Bitmap getPageBitmap(int pageIndex) { if (pageBitmaps[pageIndex] == null) pageBitmaps[pageIndex] = renderPage(pageIndex); return pageBitmaps[pageIndex]; } /** * Sets the active page index. This ensures that the active, the previous and * the next page are loaded. The other pages are removed from memory. */ public void setActivePageIndex(int activePageIndex) { for (int iPage : range(layout.getPages())) { if (iPage < activePageIndex - 2 || iPage > activePageIndex + 1) { pageBitmaps[iPage] = null; } else if (pageBitmaps[iPage] == null) { pageBitmaps[iPage] = renderPage(iPage); } } } private Bitmap renderPage(int page) { Bitmap ret = Bitmap.createBitmap(screenSize.width, screenSize.height, Config.ARGB_8888); Canvas canvas = new Canvas(ret); int background = R.drawable.paper_middle; int pagesCount = layout.getPages().size(); if (pagesCount == 1) background = R.drawable.paper_single; else if (page == 0) background = R.drawable.paper_first; else if (page == pagesCount - 1) background = R.drawable.paper_last; Bitmap bmp = BitmapFactory.decodeResource(resources, background); canvas.drawBitmap(bmp, null, canvas.getClipBounds(), null); AndroidCanvas context = new AndroidCanvas(canvas, layout.getPages().get(page).getFormat().getSize(), CanvasFormat.Raster, CanvasDecoration.None, CanvasIntegrity.Perfect); androidLayoutRenderer.paint(layout, page, context, scaling); //first page: draw title if (page == 0) { Paint paint = new Paint(); paint.setColor(Color.BLACK); paint.setStyle(Paint.Style.FILL); paint.setAntiAlias(true); paint.setTypeface(Typeface.SERIF); paint.setTextSize(titleTextHeightPx * 0.6f); float width = paint.measureText(title); canvas.drawText(title, screenSize.width / 2 - width / 2, marginPx + titleTextHeightPx, paint); } return ret; } }