apps/updf: update 2.1 (#559)
- Supersampling for crisp text, with automatic disabling on low-end CPUs (<1 GHz via fn 18.5) - snap to a clear 100% (±7%) - Zoom field in the toolbar with manual percentage input - UI tweaks/bug fixes --------- Co-authored-by: Burer <burer@kolibrios.org> Reviewed-on: #559 Reviewed-by: Burer <burer@kolibrios.org> Reviewed-by: Mikhail Frolov <mixa.frolov2003@gmail.com> Co-authored-by: leency <lipatov.kiril@gmail.com>
This commit was merged in pull request #559.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# build artifacts (uPDF / MuPDF 1.19 KolibriOS port)
|
||||
# note: build/uPDF (the final binary) is intentionally NOT ignored - it ships
|
||||
build/obj/
|
||||
build/*.log
|
||||
build/updfnew_test
|
||||
|
||||
# compiled objects, static libs, linker output anywhere in the tree
|
||||
*.o
|
||||
*.a
|
||||
*.kex
|
||||
@@ -39,6 +39,7 @@ struct proc_info Form;
|
||||
#define SC_G 0x22
|
||||
#define SC_L 0x26
|
||||
#define SC_W 0x11 // fit page to width
|
||||
#define SC_S 0x1F // toggle supersampling (sharper text)
|
||||
#define SC_NUM_MINUS 0x4A // keypad -
|
||||
#define SC_NUM_PLUS 0x4E // keypad +
|
||||
#define SC_HOME 0x47
|
||||
@@ -79,22 +80,31 @@ short show_area_x;
|
||||
char key_mode_enter_page_number;
|
||||
int new_page_number;
|
||||
|
||||
// zoom entry box between the +/- buttons (mirrors the page-number box)
|
||||
#define ZOOM_AREA_W 50
|
||||
static short zoom_area_x;
|
||||
char key_mode_enter_zoom;
|
||||
int new_zoom;
|
||||
|
||||
static char fit_pending = 1; // fit the page to width on the first draw
|
||||
|
||||
const char *help[] = {
|
||||
"Keys:",
|
||||
" ",
|
||||
"PageUp - go to previous page",
|
||||
"PageDown - go to next page",
|
||||
"Home - go to first page",
|
||||
"End - go to last page",
|
||||
"Down arrow - scroll down",
|
||||
"Up arrow - scroll up",
|
||||
"+/- - zoom in/out",
|
||||
"w - fit page to width",
|
||||
"[ or l - rotate page 90 deg to the left",
|
||||
"] or r - rotate page 90 deg to the right",
|
||||
"g - grayscale on/off",
|
||||
"PageUp / PageDown - previous / next page",
|
||||
"Home / End - first / last page",
|
||||
"Up / Down arrow - scroll",
|
||||
"+ / - - zoom in / out",
|
||||
"w - fit page to width",
|
||||
"[ or l / ] or r - rotate page left / right",
|
||||
"g - grayscale on / off",
|
||||
"s - supersampling (sharper text) on / off",
|
||||
" ",
|
||||
"Mouse:",
|
||||
"Left drag - select text (copies to clipboard)",
|
||||
"Right drag - pan the page",
|
||||
"Wheel - scroll",
|
||||
"Click page or zoom box - type a value",
|
||||
" ",
|
||||
"Press Escape to hide help",
|
||||
0
|
||||
@@ -107,6 +117,10 @@ void winblit(pdfapp_t *app);
|
||||
void DrawPagination(void);
|
||||
void HandleNewPageNumber(unsigned char key);
|
||||
void ApplyNewPageNumber(void);
|
||||
void DrawZoom(void);
|
||||
void GetNewZoom(void);
|
||||
void HandleNewZoom(unsigned char key);
|
||||
void ApplyNewZoom(void);
|
||||
void DrawMainWindow(void);
|
||||
void FlushPageCache(void);
|
||||
void SyncCacheParams(void);
|
||||
@@ -126,6 +140,8 @@ int ClientToPagePoint(int mx, int my, fz_point *pt);
|
||||
void CopySelectionToClipboard(void);
|
||||
void ProcessPageMouse(void);
|
||||
void FitPageWidth(void);
|
||||
void ToggleSupersampling(void);
|
||||
unsigned CpuBaseMhz(void);
|
||||
|
||||
|
||||
// The MuPDF 1.19 glue (pdfapp.c) only calls back into winrepaint().
|
||||
@@ -288,6 +304,7 @@ void winblit(pdfapp_t *app)
|
||||
if (Form.cwidth == 0) return; // window is not drawn yet
|
||||
|
||||
if (key_mode_enter_page_number==1) HandleNewPageNumber(0); else DrawPagination();
|
||||
if (key_mode_enter_zoom==1) HandleNewZoom(0); else DrawZoom();
|
||||
|
||||
gapp.panx = 0;
|
||||
|
||||
@@ -645,9 +662,10 @@ void NormalizeScrollPos(void)
|
||||
|
||||
void GetNewPageNumber(void)
|
||||
{
|
||||
key_mode_enter_zoom = 0; // the two input boxes are mutually exclusive
|
||||
new_page_number = gapp.pageno;
|
||||
key_mode_enter_page_number = 1;
|
||||
HandleNewPageNumber(0);
|
||||
winblit(&gapp); // repaint the page (drop any highlight) + draw the box
|
||||
}
|
||||
|
||||
void HandleNewPageNumber(unsigned char key)
|
||||
@@ -697,6 +715,81 @@ void DrawPagination(void)
|
||||
kos_text(show_area_x + show_area_w/2 - strlen(pages_display)*6/2, 14, 0x000000, pages_display, strlen(pages_display));
|
||||
}
|
||||
|
||||
// current zoom as a percentage (72 dpi == 100%)
|
||||
int ZoomPercent(void)
|
||||
{
|
||||
return (int)(gapp.resolution / 72.0f * 100.0f + 0.5f);
|
||||
}
|
||||
|
||||
void DrawZoom(void)
|
||||
{
|
||||
char s[12];
|
||||
kol_paint_bar(zoom_area_x, 6, ZOOM_AREA_W, 22, 0xF4F4F4);
|
||||
sprintf(s, "%d%%", ZoomPercent());
|
||||
kos_text(zoom_area_x + ZOOM_AREA_W/2 - strlen(s)*6/2, 14, 0x000000, s, strlen(s));
|
||||
}
|
||||
|
||||
void GetNewZoom(void)
|
||||
{
|
||||
key_mode_enter_page_number = 0; // the two input boxes are mutually exclusive
|
||||
new_zoom = 0; // type the wanted zoom from scratch
|
||||
key_mode_enter_zoom = 1;
|
||||
winblit(&gapp); // repaint the page (drop any highlight) + draw the box
|
||||
}
|
||||
|
||||
void HandleNewZoom(unsigned char key)
|
||||
{
|
||||
char lbl[12];
|
||||
|
||||
if (key >= '0' && key <= '9')
|
||||
new_zoom = new_zoom * 10 + key - '0';
|
||||
if (key == ASCII_KEY_BS)
|
||||
new_zoom /= 10;
|
||||
if (key == ASCII_KEY_ENTER)
|
||||
{
|
||||
if (new_zoom == 0) // nothing typed: cancel, like Esc
|
||||
{
|
||||
key_mode_enter_zoom = 0;
|
||||
DrawMainWindow();
|
||||
return;
|
||||
}
|
||||
ApplyNewZoom();
|
||||
return;
|
||||
}
|
||||
if (key == ASCII_KEY_ESC)
|
||||
{
|
||||
key_mode_enter_zoom = 0;
|
||||
DrawMainWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
itoa(new_zoom, lbl, 10);
|
||||
strcat(lbl, "_");
|
||||
kol_paint_bar(zoom_area_x, 6, ZOOM_AREA_W, 22, 0xFDF88E);
|
||||
kos_text(zoom_area_x + ZOOM_AREA_W/2 - strlen(lbl)*6/2, 14, 0x000000, lbl, strlen(lbl));
|
||||
|
||||
if (new_zoom > 1600) ApplyNewZoom();
|
||||
}
|
||||
|
||||
void ApplyNewZoom(void)
|
||||
{
|
||||
int oldh;
|
||||
key_mode_enter_zoom = 0;
|
||||
if (new_zoom < 10) new_zoom = 10;
|
||||
if (new_zoom > 1600) new_zoom = 1600;
|
||||
oldh = (gapp.image && gapp.image->h > 0) ? gapp.image->h : 1;
|
||||
gapp.resolution = new_zoom / 100.0f * 72.0f;
|
||||
do_not_blit = 1;
|
||||
pdfapp_showpage(&gapp, 0, 1, 0); // re-render at the new zoom
|
||||
do_not_blit = 0;
|
||||
if (gapp.image)
|
||||
{
|
||||
gapp.pany = (int)((long long)gapp.pany * gapp.image->h / oldh);
|
||||
NormalizeScrollPos();
|
||||
}
|
||||
DrawMainWindow();
|
||||
}
|
||||
|
||||
void DrawToolbarButton(int x, char image_id)
|
||||
{
|
||||
kol_btn_define(x, 5, 26-1, 24-1, 10 + image_id + BT_HIDE, 0);
|
||||
@@ -707,18 +800,27 @@ void DrawMainWindow(void)
|
||||
{
|
||||
kol_paint_bar(0, 0, Form.cwidth, TOOLBAR_HEIGHT - 1, 0xe1e1e1); // bar on the top (buttons holder)
|
||||
kol_paint_bar(0, TOOLBAR_HEIGHT - 1, Form.cwidth, 1, 0x7F7F7F);
|
||||
DrawToolbarButton(8,0); //open_folder
|
||||
DrawToolbarButton(42,1); //magnify -
|
||||
DrawToolbarButton(67,2); //magnify +
|
||||
DrawToolbarButton(101,6); //rotate left
|
||||
DrawToolbarButton(126,7); //rotate right
|
||||
DrawToolbarButton(Form.cwidth - 160,3); //show help
|
||||
show_area_x = Form.cwidth - show_area_w - 34;
|
||||
DrawToolbarButton(show_area_x - 26,4); //prev page
|
||||
DrawToolbarButton(show_area_x + show_area_w,5); //nex page
|
||||
kol_btn_define(show_area_x-1, 5, show_area_w+1, 23, 20 + BT_HIDE, 0xA4A4A4);
|
||||
kol_paint_bar(show_area_x, 5, show_area_w, 1, 0xA4A4A4);
|
||||
// left: open folder, info/help, rotate left/right
|
||||
DrawToolbarButton(8, 0); //open_folder
|
||||
DrawToolbarButton(42, 3); //info / help
|
||||
DrawToolbarButton(76, 6); //rotate left
|
||||
DrawToolbarButton(101, 7); //rotate right
|
||||
|
||||
// center: page navigation < N/M >
|
||||
show_area_x = (Form.cwidth - (26 + show_area_w + 26)) / 2 + 26;
|
||||
DrawToolbarButton(show_area_x - 26, 4); //prev page (touches box left)
|
||||
kol_btn_define(show_area_x - 1, 5, show_area_w + 1, 23, 20 + BT_HIDE, 0xA4A4A4);
|
||||
kol_paint_bar(show_area_x, 5, show_area_w, 1, 0xA4A4A4);
|
||||
kol_paint_bar(show_area_x, 28, show_area_w, 1, 0xA4A4A4);
|
||||
DrawToolbarButton(show_area_x + show_area_w, 5); //next page (touches box right)
|
||||
|
||||
// right: the zoom block ( - [100%] + ), edge-anchored
|
||||
zoom_area_x = Form.cwidth - 8 - 26 - ZOOM_AREA_W; // box left; zoom + is rightmost
|
||||
DrawToolbarButton(zoom_area_x - 26, 1); //magnify - (touches box left)
|
||||
kol_btn_define(zoom_area_x - 1, 5, ZOOM_AREA_W + 1, 23, 21 + BT_HIDE, 0xA4A4A4);
|
||||
kol_paint_bar(zoom_area_x, 5, ZOOM_AREA_W, 1, 0xA4A4A4);
|
||||
kol_paint_bar(zoom_area_x, 28, ZOOM_AREA_W, 1, 0xA4A4A4);
|
||||
DrawToolbarButton(zoom_area_x + ZOOM_AREA_W, 2); //magnify + (touches box right)
|
||||
if (fit_pending && gapp.image) // open the document fitted to width
|
||||
{
|
||||
FitPageWidth();
|
||||
@@ -783,9 +885,32 @@ void FitPageWidth(void)
|
||||
gapp.resolution = gapp.resolution * (float)target / gapp.image->w;
|
||||
if (gapp.resolution < 18) gapp.resolution = 18;
|
||||
if (gapp.resolution > 1200) gapp.resolution = 1200;
|
||||
pdfapp_snapzoom(&gapp); // 96%-ish fit -> clean 100%
|
||||
pdfapp_showpage(&gapp, 0, 1, 0); /* re-render current page, no repaint */
|
||||
}
|
||||
|
||||
// toggle 2x supersampling (sharper text) on/off at runtime; re-renders
|
||||
// the current page and drops the cache (neighbours were at the old factor)
|
||||
void ToggleSupersampling(void)
|
||||
{
|
||||
char msg[96];
|
||||
long px;
|
||||
gapp.ss_factor = (gapp.ss_factor > 1) ? 1 : 2;
|
||||
FlushPageCache();
|
||||
do_not_blit = 1;
|
||||
pdfapp_showpage(&gapp, 0, 1, 0);
|
||||
do_not_blit = 0;
|
||||
NormalizeScrollPos();
|
||||
winblit(&gapp);
|
||||
// diagnostic: gapp.image is the 1x display pixmap; if its area >= the SS
|
||||
// guard (2.5M px) supersampling is skipped even when ss_factor==2
|
||||
px = (long)gapp.image->w * gapp.image->h;
|
||||
sprintf(msg, "uPDF: SS ss_factor=%d, page %dx%d=%ld px %s\n",
|
||||
gapp.ss_factor, gapp.image->w, gapp.image->h, px,
|
||||
(gapp.ss_factor > 1 && px < 2500000) ? "-> SS ACTIVE" : "-> SS skipped");
|
||||
kol_board_puts(msg);
|
||||
}
|
||||
|
||||
void PageZoomIn(void)
|
||||
{
|
||||
int oldh = gapp.image->h;
|
||||
@@ -834,6 +959,24 @@ void PageRotateRight(void)
|
||||
winblit(&gapp);
|
||||
}
|
||||
|
||||
// CPU base frequency in MHz. sysfn 18.5 returns the clock modulo 2^32 and
|
||||
// so misreads CPUs faster than ~4.29 GHz as slow ones. CPUID leaf 16h
|
||||
// (Intel Skylake+, i.e. exactly the CPUs that can exceed 4.29 GHz) reports
|
||||
// the base frequency in MHz directly, so prefer it; CPUs old enough to
|
||||
// lack the leaf cannot wrap 18.5 anyway.
|
||||
unsigned CpuBaseMhz(void)
|
||||
{
|
||||
unsigned a, b, c, d;
|
||||
asm volatile ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0));
|
||||
if (a >= 0x16)
|
||||
{
|
||||
asm volatile ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "a"(0x16), "c"(0));
|
||||
if (a)
|
||||
return a;
|
||||
}
|
||||
return kol_system_cpufreq() / 1000000;
|
||||
}
|
||||
|
||||
int main (int argc, char* argv[])
|
||||
{
|
||||
char ii, mouse_wheels_state;
|
||||
@@ -860,6 +1003,20 @@ int main (int argc, char* argv[])
|
||||
gapp.pageno = pageno;
|
||||
pdfapp_open(&gapp, full_argv, 0, 0);
|
||||
wintitle(&gapp, 0, full_argv);
|
||||
|
||||
// Supersampling (2x render for sharper text) is ~4x the work per page.
|
||||
// On a slow CPU (< 1 GHz: PI/PII/PIII era) it would crawl, so switch it
|
||||
// off automatically. A 0/bogus reading leaves the default on (assume a
|
||||
// capable machine).
|
||||
{
|
||||
unsigned mhz = CpuBaseMhz();
|
||||
char msg[64];
|
||||
if (mhz > 0 && mhz < 1000)
|
||||
gapp.ss_factor = 1;
|
||||
sprintf(msg, "uPDF: CPU %u MHz, supersampling %s\n",
|
||||
mhz, gapp.ss_factor > 1 ? "on" : "off");
|
||||
kol_board_puts(msg);
|
||||
}
|
||||
|
||||
int butt, key, scan, ascii, screen_max_x, screen_max_y;
|
||||
kos_screen_max(&screen_max_x, &screen_max_y);
|
||||
@@ -873,11 +1030,11 @@ int main (int argc, char* argv[])
|
||||
case evReDraw:
|
||||
// gapp.shrinkwrap = 2;
|
||||
kol_paint_start();
|
||||
kol_wnd_define(
|
||||
(screen_max_x - (screen_max_y*8/10)) / 2 -15+kos_random(30),
|
||||
screen_max_y/20+kos_random(20),
|
||||
screen_max_y*8/10,
|
||||
screen_max_y*9/10,
|
||||
kol_wnd_define(
|
||||
(screen_max_x - (screen_max_y*8/10 + 20)) / 2 -15+kos_random(30),
|
||||
screen_max_y/20+kos_random(20),
|
||||
screen_max_y*8/10 + 20,
|
||||
screen_max_y*9/10,
|
||||
0x73000000, 0x800000FF, Title
|
||||
);
|
||||
kol_paint_end();
|
||||
@@ -898,12 +1055,17 @@ int main (int argc, char* argv[])
|
||||
scan = (key >> 16) & 0xFF; // layout-independent scancode
|
||||
ascii = (key >> 8) & 0xFF; // ASCII, for digit entry only
|
||||
|
||||
// page-number entry still needs literal digits/enter/bs/esc
|
||||
// page-number / zoom entry needs literal digits/enter/bs/esc
|
||||
if (key_mode_enter_page_number)
|
||||
{
|
||||
HandleNewPageNumber(ascii);
|
||||
break;
|
||||
}
|
||||
if (key_mode_enter_zoom)
|
||||
{
|
||||
HandleNewZoom(ascii);
|
||||
break;
|
||||
}
|
||||
|
||||
ClearSelection(); // any hotkey drops the current selection
|
||||
|
||||
@@ -916,6 +1078,7 @@ int main (int argc, char* argv[])
|
||||
case SC_END: pdfapp_onkey(&gapp, 'G'); break; // last page
|
||||
case SC_G: pdfapp_onkey(&gapp, 'c'); break; // grayscale on/off
|
||||
case SC_W: FitPageWidth(); NormalizeScrollPos(); winblit(&gapp); break; // fit width
|
||||
case SC_S: ToggleSupersampling(); break; // sharper text on/off
|
||||
case SC_L:
|
||||
case SC_LBRACKET: PageRotateLeft(); break;
|
||||
case SC_R:
|
||||
@@ -939,8 +1102,8 @@ int main (int argc, char* argv[])
|
||||
if(butt==13) //show help
|
||||
{
|
||||
kol_paint_bar(0, TOOLBAR_HEIGHT, Form.cwidth, Form.cheight - TOOLBAR_HEIGHT, 0xF2F2F2);
|
||||
kos_text(20, TOOLBAR_HEIGHT + 20, 0x81000000, "uPDF for KolibriOS v2.0", 0);
|
||||
kos_text(21, TOOLBAR_HEIGHT + 20, 0x81000000, "uPDF for KolibriOS v2.0", 0);
|
||||
kos_text(20, TOOLBAR_HEIGHT + 20, 0x81000000, "uPDF for KolibriOS v2.1", 0);
|
||||
kos_text(21, TOOLBAR_HEIGHT + 20, 0x81000000, "uPDF for KolibriOS v2.1", 0);
|
||||
for (ii=0; help[ii]!=0; ii++) {
|
||||
kos_text(20, TOOLBAR_HEIGHT + 60 + ii * 20, 0x90000000, help[ii], 0);
|
||||
}
|
||||
@@ -950,6 +1113,7 @@ int main (int argc, char* argv[])
|
||||
if(butt==16) PageRotateLeft();
|
||||
if(butt==17) PageRotateRight();
|
||||
if(butt==20) GetNewPageNumber();
|
||||
if(butt==21) GetNewZoom();
|
||||
break;
|
||||
|
||||
case evMouse:
|
||||
|
||||
@@ -74,37 +74,110 @@ static void desaturate(fz_pixmap *pix)
|
||||
}
|
||||
}
|
||||
|
||||
/* Supersampling factor for sharper text: render the page at SS_FACTOR times
|
||||
the display resolution, then average each SS*SS block down. This is far
|
||||
crisper than rendering straight at the low fit-width DPI. The oversized
|
||||
buffer is transient - the cached/displayed pixmap is normal 1x size, so
|
||||
RAM use does not grow. Disabled automatically when the 1x page is already
|
||||
large (high zoom) to avoid running the KolibriOS heap out of memory. */
|
||||
#define SS_FACTOR 2
|
||||
#define SS_MAX_PIXELS 2500000 /* above this many 1x pixels, skip supersampling */
|
||||
|
||||
/* Box-average src (BGRA, n>=4) down by factor f into dst. */
|
||||
static void downsample_box(fz_pixmap *dst, fz_pixmap *src, int f)
|
||||
{
|
||||
int n = dst->n, dw = dst->w, dh = dst->h, x, y, k, i, j;
|
||||
int area = f * f;
|
||||
|
||||
for (y = 0; y < dh; y++)
|
||||
{
|
||||
unsigned char *drow = dst->samples + (ptrdiff_t)y * dst->stride;
|
||||
for (x = 0; x < dw; x++)
|
||||
{
|
||||
for (k = 0; k < n; k++)
|
||||
{
|
||||
int sum = 0;
|
||||
for (j = 0; j < f; j++)
|
||||
{
|
||||
int sy = y * f + j;
|
||||
unsigned char *srow;
|
||||
if (sy >= src->h) sy = src->h - 1;
|
||||
srow = src->samples + (ptrdiff_t)sy * src->stride;
|
||||
for (i = 0; i < f; i++)
|
||||
{
|
||||
int sx = x * f + i;
|
||||
if (sx >= src->w) sx = src->w - 1;
|
||||
sum += srow[sx * src->n + k];
|
||||
}
|
||||
}
|
||||
drow[x * n + k] = (unsigned char)((sum + area / 2) / area);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Render a page into a fresh BGRA pixmap on a WHITE background.
|
||||
fz_new_pixmap_from_page(alpha=1) clears to transparent black; since the
|
||||
KolibriOS blitter has no alpha it then shows a black page for any PDF that
|
||||
doesn't paint its own background. So we allocate the pixmap ourselves,
|
||||
clear it to opaque white, and run the page over it. Alpha stays (n=4)
|
||||
because the blitter needs 32bpp. */
|
||||
doesn't paint its own background. So we clear to opaque white ourselves.
|
||||
Alpha stays (n=4) because the blitter needs 32bpp. */
|
||||
static fz_pixmap *render_bgra_white(pdfapp_t *app, fz_page *page, fz_matrix ctm)
|
||||
{
|
||||
fz_irect bbox;
|
||||
fz_pixmap *pix;
|
||||
fz_context *ctx = app->ctx;
|
||||
fz_rect bound = fz_bound_page(ctx, page);
|
||||
fz_irect box1 = fz_round_rect(fz_transform_rect(bound, ctm));
|
||||
long pixels = (long)(box1.x1 - box1.x0) * (box1.y1 - box1.y0);
|
||||
int want = app->ss_factor < 1 ? 1 : app->ss_factor;
|
||||
int ss = (want > 1 && pixels > 0 && pixels < SS_MAX_PIXELS) ? want : 1;
|
||||
|
||||
fz_matrix rctm = (ss > 1) ? fz_pre_scale(ctm, (float)ss, (float)ss) : ctm;
|
||||
fz_irect rbox = (ss > 1) ? fz_round_rect(fz_transform_rect(bound, rctm)) : box1;
|
||||
fz_pixmap *super = NULL, *dst = NULL;
|
||||
fz_device *dev = NULL;
|
||||
|
||||
bbox = fz_round_rect(fz_transform_rect(fz_bound_page(app->ctx, page), ctm));
|
||||
pix = fz_new_pixmap_with_bbox(app->ctx, fz_device_bgr(app->ctx), bbox, NULL, 1);
|
||||
|
||||
fz_var(super);
|
||||
fz_var(dst);
|
||||
fz_var(dev);
|
||||
fz_try(app->ctx)
|
||||
fz_try(ctx)
|
||||
{
|
||||
fz_clear_pixmap_with_value(app->ctx, pix, 0xff); /* opaque white paper */
|
||||
dev = fz_new_draw_device(app->ctx, fz_identity, pix);
|
||||
fz_run_page(app->ctx, page, dev, ctm, NULL);
|
||||
fz_close_device(app->ctx, dev);
|
||||
super = fz_new_pixmap_with_bbox(ctx, fz_device_bgr(ctx), rbox, NULL, 1);
|
||||
fz_clear_pixmap_with_value(ctx, super, 0xff); /* opaque white paper */
|
||||
dev = fz_new_draw_device(ctx, fz_identity, super);
|
||||
fz_run_page(ctx, page, dev, rctm, NULL);
|
||||
fz_close_device(ctx, dev);
|
||||
|
||||
if (ss > 1)
|
||||
{
|
||||
fz_irect dbox;
|
||||
dbox.x0 = rbox.x0 / ss; dbox.y0 = rbox.y0 / ss;
|
||||
dbox.x1 = dbox.x0 + (rbox.x1 - rbox.x0) / ss;
|
||||
dbox.y1 = dbox.y0 + (rbox.y1 - rbox.y0) / ss;
|
||||
dst = fz_new_pixmap_with_bbox(ctx, fz_device_bgr(ctx), dbox, NULL, 1);
|
||||
downsample_box(dst, super, ss);
|
||||
}
|
||||
else
|
||||
{
|
||||
dst = fz_keep_pixmap(ctx, super);
|
||||
}
|
||||
}
|
||||
fz_always(app->ctx)
|
||||
fz_drop_device(app->ctx, dev);
|
||||
fz_catch(app->ctx)
|
||||
fz_always(ctx)
|
||||
{
|
||||
fz_drop_pixmap(app->ctx, pix);
|
||||
fz_rethrow(app->ctx);
|
||||
fz_drop_device(ctx, dev);
|
||||
fz_drop_pixmap(ctx, super);
|
||||
}
|
||||
return pix;
|
||||
fz_catch(ctx)
|
||||
{
|
||||
fz_drop_pixmap(ctx, dst);
|
||||
if (ss > 1)
|
||||
{
|
||||
/* usually out of memory - the supersampled buffer is ss^2
|
||||
times the 1x size; retry plain and stop supersampling */
|
||||
app->ss_factor = 1;
|
||||
return render_bgra_white(app, page, ctm);
|
||||
}
|
||||
fz_rethrow(ctx);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void pdfapp_loadpage(pdfapp_t *app)
|
||||
@@ -131,6 +204,7 @@ void pdfapp_init(pdfapp_t *app)
|
||||
memset(app, 0, sizeof(*app));
|
||||
app->resolution = 72;
|
||||
app->pageno = 1;
|
||||
app->ss_factor = SS_FACTOR; /* the GUI lowers this on a slow CPU */
|
||||
app->ctx = fz_new_context(NULL, NULL, STORE_LIMIT);
|
||||
if (app->ctx)
|
||||
fz_register_document_handlers(app->ctx);
|
||||
@@ -238,6 +312,16 @@ void pdfapp_showpage(pdfapp_t *app, int loadpage, int drawpage, int repaint)
|
||||
|
||||
/* ---- input ----------------------------------------------------------- */
|
||||
|
||||
/* Snap to exactly 100% (72 dpi) when the zoom lands within ~7% of it, so
|
||||
stepping with +/- and fit-to-width give a clean 100% instead of 96%/101%. */
|
||||
#define SNAP_BAND 0.07f
|
||||
void pdfapp_snapzoom(pdfapp_t *app)
|
||||
{
|
||||
if (app->resolution > 72.0f * (1.0f - SNAP_BAND) &&
|
||||
app->resolution < 72.0f * (1.0f + SNAP_BAND))
|
||||
app->resolution = 72.0f;
|
||||
}
|
||||
|
||||
void pdfapp_onkey(pdfapp_t *app, int c)
|
||||
{
|
||||
switch (c)
|
||||
@@ -246,11 +330,13 @@ void pdfapp_onkey(pdfapp_t *app, int c)
|
||||
case '=':
|
||||
app->resolution *= ZOOMSTEP;
|
||||
if (app->resolution > MAXDPI) app->resolution = MAXDPI;
|
||||
pdfapp_snapzoom(app);
|
||||
pdfapp_showpage(app, 0, 1, 1);
|
||||
break;
|
||||
case '-':
|
||||
app->resolution /= ZOOMSTEP;
|
||||
if (app->resolution < MINDPI) app->resolution = MINDPI;
|
||||
pdfapp_snapzoom(app);
|
||||
pdfapp_showpage(app, 0, 1, 1);
|
||||
break;
|
||||
case 'L':
|
||||
|
||||
@@ -30,6 +30,7 @@ struct pdfapp_s
|
||||
float resolution; /* dpi; 72 == 100% */
|
||||
int rotate; /* degrees */
|
||||
int grayscale;
|
||||
int ss_factor; /* supersampling factor (1 = off) for sharper text */
|
||||
|
||||
/* scroll offset (managed by the GUI) */
|
||||
int panx, pany;
|
||||
@@ -55,6 +56,9 @@ fz_pixmap *pdfapp_renderpage(pdfapp_t *app, int pageno);
|
||||
( + - L R c g G [ ] ) */
|
||||
void pdfapp_onkey(pdfapp_t *app, int c);
|
||||
|
||||
/* snap the current zoom to exactly 100% if it is within ~7% of it */
|
||||
void pdfapp_snapzoom(pdfapp_t *app);
|
||||
|
||||
/* selection: a/b are in page space; the GUI supplies them from clicks */
|
||||
void pdfapp_invertselection(pdfapp_t *app); /* toggle highlight in image */
|
||||
char *pdfapp_copyselection(pdfapp_t *app); /* fz-allocated UTF-8, or NULL */
|
||||
|
||||
Binary file not shown.
@@ -1,13 +0,0 @@
|
||||
# editorconfig.org settings for mupdf project
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
|
||||
[memento.*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
@@ -1,32 +0,0 @@
|
||||
# Macros:
|
||||
[attr]tabs whitespace=trailing-space,space-before-tab,indent-with-non-tab,tabwidth=4
|
||||
[attr]spaces whitespace=trailing-space,space-before-tab,tabs-in-indent
|
||||
[attr]makefile whitespace=trailing-space,space-before-tab,indent-with-non-tab,tabwidth=8
|
||||
|
||||
# Source files:
|
||||
* text=auto
|
||||
Make* makefile
|
||||
*.mk makefile
|
||||
*.make makefile
|
||||
*.[chm] tabs
|
||||
*.java tabs
|
||||
*.xml tabs
|
||||
*.props spaces
|
||||
*.targets spaces
|
||||
*.vcxproj spaces
|
||||
*.vcxproj.filters spaces
|
||||
bin2coff.xml spaces
|
||||
|
||||
# Documentation files:
|
||||
*.txt spaces
|
||||
|
||||
# Legacy source files:
|
||||
memento.* spaces
|
||||
|
||||
# Generated source files:
|
||||
source/html/css-properties.h spaces
|
||||
|
||||
# iOS project files:
|
||||
*.xib spaces
|
||||
*.xcscheme spaces
|
||||
*.json spaces
|
||||
@@ -1,48 +0,0 @@
|
||||
# Optional thirdparty libraryes
|
||||
thirdparty/luratech
|
||||
thirdparty/jpegxr
|
||||
thirdparty/mapping-resources-pdf
|
||||
thirdparty/cmap-resources
|
||||
|
||||
# Generated files:
|
||||
build
|
||||
generated/resources/fonts/droid
|
||||
generated/resources/fonts/han
|
||||
generated/resources/fonts/noto
|
||||
generated/resources/fonts/sil
|
||||
tags
|
||||
cscope.*
|
||||
|
||||
# Editor and file browser turds:
|
||||
*~
|
||||
.*.swp
|
||||
.DS_Store
|
||||
\#*\#
|
||||
|
||||
# Editor settings:
|
||||
.exrc
|
||||
.vimrc
|
||||
*.xcworkspace
|
||||
*.xcuserdatad
|
||||
user.make
|
||||
guide
|
||||
|
||||
# Temporary and build files:
|
||||
*.class
|
||||
*.jar
|
||||
*.o
|
||||
*.so
|
||||
*.dll
|
||||
DerivedData
|
||||
|
||||
platform/java/local.properties
|
||||
platform/win32/*.user
|
||||
platform/win32/*.ncb
|
||||
platform/win32/*.suo
|
||||
platform/win32/Debug
|
||||
platform/win32/DebugOpenssl
|
||||
platform/win32/Profile
|
||||
platform/win32/Release
|
||||
platform/win32/ReleaseOpenssl
|
||||
platform/win32/Memento
|
||||
platform/win32/x64
|
||||
@@ -1,42 +0,0 @@
|
||||
[submodule "thirdparty/jbig2dec"]
|
||||
path = thirdparty/jbig2dec
|
||||
url = ../jbig2dec.git
|
||||
[submodule "thirdparty/mujs"]
|
||||
path = thirdparty/mujs
|
||||
url = ../mujs.git
|
||||
[submodule "thirdparty/freetype"]
|
||||
path = thirdparty/freetype
|
||||
url = ../thirdparty-freetype2.git
|
||||
[submodule "thirdparty/gumbo-parser"]
|
||||
path = thirdparty/gumbo-parser
|
||||
url = ../thirdparty-gumbo-parser.git
|
||||
[submodule "thirdparty/harfbuzz"]
|
||||
path = thirdparty/harfbuzz
|
||||
url = ../thirdparty-harfbuzz.git
|
||||
[submodule "thirdparty/jpeg"]
|
||||
path = thirdparty/libjpeg
|
||||
url = ../thirdparty-libjpeg.git
|
||||
[submodule "thirdparty/lcms2"]
|
||||
path = thirdparty/lcms2
|
||||
url = ../thirdparty-lcms2.git
|
||||
[submodule "thirdparty/openjpeg"]
|
||||
path = thirdparty/openjpeg
|
||||
url = ../thirdparty-openjpeg.git
|
||||
[submodule "thirdparty/zlib"]
|
||||
path = thirdparty/zlib
|
||||
url = ../thirdparty-zlib.git
|
||||
[submodule "thirdparty/curl"]
|
||||
path = thirdparty/curl
|
||||
url = ../thirdparty-curl.git
|
||||
[submodule "thirdparty/freeglut"]
|
||||
path = thirdparty/freeglut
|
||||
url = ../thirdparty-freeglut.git
|
||||
[submodule "thirdparty/tesseract"]
|
||||
path = thirdparty/tesseract
|
||||
url = ../thirdparty-tesseract.git
|
||||
[submodule "thirdparty/leptonica"]
|
||||
path = thirdparty/leptonica
|
||||
url = ../thirdparty-leptonica.git
|
||||
[submodule "thirdparty/extract"]
|
||||
path = thirdparty/extract
|
||||
url = ../extract.git
|
||||
@@ -0,0 +1,4 @@
|
||||
LIBJPEG_9.0 {
|
||||
global:
|
||||
*;
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
ZLIB_1.2.0 {
|
||||
global:
|
||||
compressBound;
|
||||
deflateBound;
|
||||
inflateBack;
|
||||
inflateBackEnd;
|
||||
inflateBackInit_;
|
||||
inflateCopy;
|
||||
local:
|
||||
deflate_copyright;
|
||||
inflate_copyright;
|
||||
inflate_fast;
|
||||
inflate_table;
|
||||
zcalloc;
|
||||
zcfree;
|
||||
z_errmsg;
|
||||
gz_error;
|
||||
gz_intmax;
|
||||
_*;
|
||||
};
|
||||
|
||||
ZLIB_1.2.0.2 {
|
||||
gzclearerr;
|
||||
gzungetc;
|
||||
zlibCompileFlags;
|
||||
} ZLIB_1.2.0;
|
||||
|
||||
ZLIB_1.2.0.8 {
|
||||
deflatePrime;
|
||||
} ZLIB_1.2.0.2;
|
||||
|
||||
ZLIB_1.2.2 {
|
||||
adler32_combine;
|
||||
crc32_combine;
|
||||
deflateSetHeader;
|
||||
inflateGetHeader;
|
||||
} ZLIB_1.2.0.8;
|
||||
|
||||
ZLIB_1.2.2.3 {
|
||||
deflateTune;
|
||||
gzdirect;
|
||||
} ZLIB_1.2.2;
|
||||
|
||||
ZLIB_1.2.2.4 {
|
||||
inflatePrime;
|
||||
} ZLIB_1.2.2.3;
|
||||
|
||||
ZLIB_1.2.3.3 {
|
||||
adler32_combine64;
|
||||
crc32_combine64;
|
||||
gzopen64;
|
||||
gzseek64;
|
||||
gztell64;
|
||||
inflateUndermine;
|
||||
} ZLIB_1.2.2.4;
|
||||
|
||||
ZLIB_1.2.3.4 {
|
||||
inflateReset2;
|
||||
inflateMark;
|
||||
} ZLIB_1.2.3.3;
|
||||
|
||||
ZLIB_1.2.3.5 {
|
||||
gzbuffer;
|
||||
gzoffset;
|
||||
gzoffset64;
|
||||
gzclose_r;
|
||||
gzclose_w;
|
||||
} ZLIB_1.2.3.4;
|
||||
|
||||
ZLIB_1.2.5.1 {
|
||||
deflatePending;
|
||||
} ZLIB_1.2.3.5;
|
||||
|
||||
ZLIB_1.2.5.2 {
|
||||
deflateResetKeep;
|
||||
gzgetc_;
|
||||
inflateResetKeep;
|
||||
} ZLIB_1.2.5.1;
|
||||
|
||||
ZLIB_1.2.7.1 {
|
||||
inflateGetDictionary;
|
||||
gzvprintf;
|
||||
} ZLIB_1.2.5.2;
|
||||
|
||||
ZLIB_1.2.9 {
|
||||
inflateCodesUsed;
|
||||
inflateValidate;
|
||||
uncompress2;
|
||||
gzfread;
|
||||
gzfwrite;
|
||||
deflateGetDictionary;
|
||||
adler32_z;
|
||||
crc32_z;
|
||||
} ZLIB_1.2.7.1;
|
||||
+1
-1
@@ -271,7 +271,7 @@ extra_files = {
|
||||
{"kolibrios/media/fplay", "common/media/fplay"},
|
||||
{"kolibrios/media/fplay_run", "common/media/fplay_run"},
|
||||
{"kolibrios/media/minimp3", "common/media/minimp3"},
|
||||
{"kolibrios/media/updf", "common/media/updf"},
|
||||
{"kolibrios/media/updf", SRC .. "/contrib/media/updf/build/uPDF"},
|
||||
{"kolibrios/media/vttf", "common/media/vttf"},
|
||||
{"kolibrios/media/beat/Beat", SRC_PROGS .. "/media/Beat/Beat"},
|
||||
{"kolibrios/media/beat/Beep1.raw", SRC_PROGS .. "/media/Beat/Beep1.raw"},
|
||||
|
||||
Reference in New Issue
Block a user