📄 displaymodel.cc.svn-base
字号:
/* Copyright Krzysztof Kowalczyk 2006-2007
License: GPLv2 */
/* How to think of display logic: physical screen of size
drawAreaSize is a window into (possibly much larger)
total area (canvas) of size canvasSize.
In DM_SINGLE_PAGE mode total area is the size of currently displayed page
given current zoomLovel and rotation.
In DM_CONTINUOUS mode total area consist of all pages rendered sequentially
with a given zoomLevel and rotation. totalAreaDy is the sum of heights
of all pages plus spaces between them and totalAreaDx is the size of
the widest page.
A possible configuration could look like this:
-----------------------------------
| |
| ------------- |
| | screen | |
| | i.e. | |
| | drawArea | |
| ------------- |
| |
| |
| canvas |
| |
| |
| |
| |
-----------------------------------
We calculate the total area size and position of each page we display on the
canvas. Canvas has to be >= draw area.
Changing zoomLevel or rotation requires recalculation of total area and
position of pdf pages in it.
We keep the offset of draw area relative to total area. The offset changes
due to scrolling (with keys or using scrollbars).
To draw we calculate which part of each page overlaps draw area, we render
those pages to a bitmap and display those bitmaps.
*/
#include "DisplayModel.h"
#include "str_util.h"
#include "wstr_util.h"
#include <assert.h>
#include <stdlib.h>
#ifdef _WIN32
#define PREDICTIVE_RENDER 1
#endif
#define MAX_BITMAPS_CACHED 256
static CRITICAL_SECTION cacheMutex;
static int cacheMutexInitialized = 0;
static BitmapCacheEntry * gBitmapCache[MAX_BITMAPS_CACHED] = {0};
static int gBitmapCacheCount = 0;
DisplaySettings gDisplaySettings = {
PADDING_PAGE_BORDER_TOP_DEF,
PADDING_PAGE_BORDER_BOTTOM_DEF,
PADDING_PAGE_BORDER_LEFT_DEF,
PADDING_PAGE_BORDER_RIGHT_DEF,
PADDING_BETWEEN_PAGES_X_DEF,
PADDING_BETWEEN_PAGES_Y_DEF
};
bool validZoomReal(double zoomReal)
{
if ((zoomReal < ZOOM_MIN) || (zoomReal > ZOOM_MAX)) {
DBG_OUT("validZoomReal() invalid zoom: %.4f\n", zoomReal);
return false;
}
return true;
}
bool displayModeFacing(DisplayMode displayMode)
{
if ((DM_SINGLE_PAGE == displayMode) || (DM_CONTINUOUS == displayMode))
return false;
else if ((DM_FACING == displayMode) || (DM_CONTINUOUS_FACING == displayMode))
return true;
assert(0);
return false;
}
bool displayModeContinuous(DisplayMode displayMode)
{
if ((DM_SINGLE_PAGE == displayMode) || (DM_FACING == displayMode))
return false;
else if ((DM_CONTINUOUS == displayMode) || (DM_CONTINUOUS_FACING == displayMode))
return true;
assert(0);
return false;
}
int columnsFromDisplayMode(DisplayMode displayMode)
{
if (DM_SINGLE_PAGE == displayMode) {
return 1;
} else if (DM_FACING == displayMode) {
return 2;
} else if (DM_CONTINUOUS == displayMode) {
return 1;
} else if (DM_CONTINUOUS_FACING == displayMode) {
return 2;
} else
assert(0);
return 1;
}
DisplaySettings *globalDisplaySettings(void)
{
return &gDisplaySettings;
}
bool rotationFlipped(int rotation)
{
assert(validRotation(rotation));
normalizeRotation(&rotation);
if ((90 == rotation) || (270 == rotation))
return true;
return false;
}
bool displayStateFromDisplayModel(DisplayState *ds, DisplayModel *dm)
{
const char *fileNameUtf8 = wstr_to_utf8(dm->fileName());
ds->filePath = str_escape(fileNameUtf8);
if (!ds->filePath)
return FALSE;
ds->displayMode = dm->displayMode();
ds->pageNo = dm->currentPageNo();
ds->rotation = dm->rotation();
ds->zoomVirtual = dm->zoomVirtual();
ds->scrollX = (int)dm->areaOffset.x;
if (displayModeContinuous(dm->displayMode())) {
/* TODO: should be offset of top page */
PdfPageInfo *pageInfo = dm->getPageInfo(ds->pageNo);
if (pageInfo)
ds->scrollY = (int)(dm->areaOffset.y + PADDING_PAGE_BORDER_TOP - pageInfo->currPosY);
else
ds->scrollY = 0;
} else {
ds->scrollY = (int)dm->areaOffset.y;
}
ds->showToc = dm->_showToc;
return TRUE;
}
/* Given 'pageInfo', which should contain correct information about
pageDx, pageDy and rotation, return a page size after applying a global
rotation */
void pageSizeAfterRotation(PdfPageInfo *pageInfo, int rotation,
double *pageDxOut, double *pageDyOut)
{
assert(pageInfo && pageDxOut && pageDyOut);
if (!pageInfo || !pageDxOut || !pageDyOut)
return;
*pageDxOut = pageInfo->pageDx;
*pageDyOut = pageInfo->pageDy;
rotation = rotation + pageInfo->rotation;
normalizeRotation(&rotation);
if (rotationFlipped(rotation))
swap_double(pageDxOut, pageDyOut);
}
DisplayModel::DisplayModel(DisplayMode displayMode)
{
_displayMode = displayMode;
_rotation = INVALID_ROTATION;
_zoomVirtual = INVALID_ZOOM;
_fullScreen = FALSE;
_showToc = TRUE;
_startPage = INVALID_PAGE_NO;
_appData = NULL;
pdfEngine = NULL;
_pdfSearch = NULL;
_pagesInfo = NULL;
_links = NULL;
_linksCount = 0;
searchHitPageNo = INVALID_PAGE_NO;
searchState.searchState = eSsNone;
pdfEngine = new PdfEngine();
_pdfSearch = new PdfSearch(pdfEngine);
}
DisplayModel::~DisplayModel()
{
free(_pagesInfo);
free(_links);
delete _pdfSearch;
delete pdfEngine;
RenderQueue_RemoveForDisplayModel(this);
BitmapCache_FreeForDisplayModel(this);
cancelRenderingForDisplayModel(this);
}
PdfPageInfo *DisplayModel::getPageInfo(int pageNo) const
{
assert(validPageNo(pageNo));
assert(_pagesInfo);
if (!_pagesInfo) return NULL;
return &(_pagesInfo[pageNo-1]);
}
bool DisplayModel::load(const WCHAR *fileName, int startPage, WindowInfo *win, bool tryrepair)
{
assert(fileName);
if (!pdfEngine->load(fileName, win, tryrepair))
return false;
if (validPageNo(startPage))
_startPage = startPage;
else
_startPage = 1;
if (!buildPagesInfo())
return false;
_pdfSearch->tracker = (PdfSearchTracker *)win;
return true;
}
bool DisplayModel::buildPagesInfo(void)
{
assert(!_pagesInfo);
int _pageCount = pageCount();
_pagesInfo = (PdfPageInfo*)calloc(1, _pageCount * sizeof(PdfPageInfo));
if (!_pagesInfo)
return false;
for (int pageNo = 1; pageNo <= _pageCount; pageNo++) {
PdfPageInfo *pageInfo = getPageInfo(pageNo);
SizeD pageSize = pdfEngine->pageSize(pageNo);
pageInfo->pageDx = pageSize.dx();
pageInfo->pageDy = pageSize.dy();
pageInfo->rotation = pdfEngine->pageRotation(pageNo);
// TODO: poppler removal. Do I need this at all?
// pageInfo->links = NULL;
pageInfo->visible = false;
pageInfo->shown = false;
if (displayModeContinuous(_displayMode)) {
pageInfo->shown = true;
} else {
if ((pageNo >= _startPage) && (pageNo < _startPage + columnsFromDisplayMode(_displayMode))) {
DBG_OUT("DisplayModelSplash::CreateFromPdfDoc() set page %d as shown\n", pageNo);
pageInfo->shown = true;
}
}
}
return true;
}
bool DisplayModel::pageShown(int pageNo)
{
PdfPageInfo *pageInfo = getPageInfo(pageNo);
if (!pageInfo)
return false;
return pageInfo->shown;
}
bool DisplayModel::pageVisible(int pageNo)
{
PdfPageInfo *pageInfo = getPageInfo(pageNo);
if (!pageInfo)
return false;
return pageInfo->visible;
}
/* Return true if a page is visible or a page below or above is visible */
bool DisplayModel::pageVisibleNearby(int pageNo)
{
/* TODO: should it check 2 pages above and below in facing mode? */
if (pageVisible(pageNo))
return true;
if (validPageNo(pageNo-1) && pageVisible(pageNo-1))
return true;
if (validPageNo(pageNo+1) && pageVisible(pageNo+1))
return true;
return false;
}
/* Given a zoom level that can include a "virtual" zoom levels like ZOOM_FIT_WIDTH
and ZOOM_FIT_PAGE, calculate an absolute zoom level */
double DisplayModel::zoomRealFromFirtualForPage(double zoomVirtual, int pageNo)
{
double _zoomReal, zoomX, zoomY, pageDx, pageDy;
double areaForPageDx, areaForPageDy;
int areaForPageDxInt;
int columns;
pageSizeAfterRotation(getPageInfo(pageNo), rotation(), &pageDx, &pageDy);
assert(0 != (int)pageDx);
assert(0 != (int)pageDy);
columns = columnsFromDisplayMode(displayMode());
areaForPageDx = (drawAreaSize.dx() - PADDING_PAGE_BORDER_LEFT - PADDING_PAGE_BORDER_RIGHT);
areaForPageDx -= (PADDING_BETWEEN_PAGES_X * (columns - 1));
areaForPageDxInt = (int)(areaForPageDx / columns);
areaForPageDx = (double)areaForPageDxInt;
areaForPageDy = drawAreaSize.dy() - PADDING_PAGE_BORDER_TOP - PADDING_PAGE_BORDER_BOTTOM;
if (ZOOM_FIT_WIDTH == zoomVirtual) {
/* TODO: should use gWinDx if we don't show scrollbarY */
if (areaForPageDx <= 0) {
return 0;
}
_zoomReal = (areaForPageDx * 100.0) / (double)pageDx;
} else if (ZOOM_FIT_PAGE == zoomVirtual) {
if (areaForPageDx <= 0 || areaForPageDy <= 0) {
return 0;
}
zoomX = (areaForPageDx * 100.0) / (double)pageDx;
zoomY = (areaForPageDy * 100.0) / (double)pageDy;
if (zoomX < zoomY)
_zoomReal = zoomX;
else
_zoomReal= zoomY;
} else
_zoomReal = zoomVirtual;
return _zoomReal;
}
int DisplayModel::firstVisiblePageNo(void) const
{
assert(_pagesInfo);
if (!_pagesInfo) return INVALID_PAGE_NO;
for (int pageNo = 1; pageNo <= pageCount(); ++pageNo) {
PdfPageInfo *pageInfo = getPageInfo(pageNo);
if (pageInfo->visible)
return pageNo;
}
/* If no pages are visible */
return INVALID_PAGE_NO;
}
int DisplayModel::currentPageNo(void) const
{
if (displayModeContinuous(displayMode()))
return firstVisiblePageNo();
else
return _startPage;
}
void DisplayModel::setZoomVirtual(double zoomVirtual)
{
int pageNo;
double minZoom = INVALID_BIG_ZOOM;
double thisPageZoom;
assert(ValidZoomVirtual(zoomVirtual));
_zoomVirtual = zoomVirtual;
if ((ZOOM_FIT_WIDTH == zoomVirtual) || (ZOOM_FIT_PAGE == zoomVirtual)) {
/* we want the same zoom for all pages, so use the smallest zoom
across the pages so that the largest page fits. In most PDFs all
pages are the same size anyway */
for (pageNo = 1; pageNo <= pageCount(); pageNo++) {
if (pageShown(pageNo)) {
thisPageZoom = zoomRealFromFirtualForPage(this->zoomVirtual(), pageNo);
if (minZoom > thisPageZoom)
minZoom = thisPageZoom;
}
}
assert(minZoom != INVALID_BIG_ZOOM);
this->_zoomReal = minZoom;
} else
this->_zoomReal = zoomVirtual;
}
/* Given pdf info and zoom/rotation, calculate the position of each page on a
large sheet that is continous view. Needs to be recalculated when:
* zoom changes
* rotation changes
* switching between display modes
* navigating to another page in non-continuous mode */
void DisplayModel::relayout(double zoomVirtual, int rotation)
{
int pageNo;
PdfPageInfo*pageInfo = NULL;
double currPosX;
double pageDx=0, pageDy=0;
int currDxInt, currDyInt;
double totalAreaDx, totalAreaDy;
double areaPerPageDx;
int areaPerPageDxInt;
double thisRowDx;
double rowMaxPageDy;
double offX, offY;
double pageOffX;
int columnsLeft;
int pageInARow;
int columns;
double newAreaOffsetX;
assert(_pagesInfo);
if (!_pagesInfo)
return;
normalizeRotation(&rotation);
assert(validRotation(rotation));
_rotation = rotation;
double currPosY = PADDING_PAGE_BORDER_TOP;
double currZoomReal = _zoomReal;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -