summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Vrhel <michael.vrhel@artifex.com>2013-03-26 23:15:02 -0700
committerRobin Watts <robin.watts@artifex.com>2013-05-16 19:25:34 +0100
commitaa0c80c1bd70cff8727e83996fc555461ef68278 (patch)
tree4dfd17f64fca38cc7c148aaef6926467684370ab
parent23575e94b917921484b824d4cbd920d386a8d971 (diff)
downloadmupdf-aa0c80c1bd70cff8727e83996fc555461ef68278.tar.xz
Fix a number of crashing issues dealing with create_task
-rw-r--r--winRT/winapp/MainPage.xaml3
-rw-r--r--winRT/winapp/MainPage.xaml.cpp639
-rw-r--r--winRT/winapp/MainPage.xaml.h44
3 files changed, 526 insertions, 160 deletions
diff --git a/winRT/winapp/MainPage.xaml b/winRT/winapp/MainPage.xaml
index a25a4fe1..785c6ec4 100644
--- a/winRT/winapp/MainPage.xaml
+++ b/winRT/winapp/MainPage.xaml
@@ -60,7 +60,8 @@
<Grid x:Name="xaml_MainGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SizeChanged="GridSizeChanged">
<Canvas x:Name="xaml_zoomCanvas" HorizontalAlignment="Center" VerticalAlignment="Center" DoubleTapped="Canvas_Double"
ManipulationDelta="Canvas_ManipulationDelta" ManipulationStarted="Canvas_ManipulationStarted"
- ManipulationStarting="Canvas_ManipulationStarting" ManipulationCompleted="Canvas_ManipulationCompleted" ManipulationMode="All">
+ ManipulationStarting="Canvas_ManipulationStarting" ManipulationCompleted="Canvas_ManipulationCompleted"
+ ManipulationMode="All">
<FlipView x:Name="xaml_horiz_flipView" SelectionChanged="FlipView_SelectionChanged" DoubleTapped="FlipView_Double" VerticalAlignment="Center"
HorizontalAlignment="Center">
<FlipView.ItemsPanel>
diff --git a/winRT/winapp/MainPage.xaml.cpp b/winRT/winapp/MainPage.xaml.cpp
index c9cbdbc5..2f9a1c7d 100644
--- a/winRT/winapp/MainPage.xaml.cpp
+++ b/winRT/winapp/MainPage.xaml.cpp
@@ -6,14 +6,17 @@
#include "pch.h"
#include "MainPage.xaml.h"
-#define LOOK_AHEAD 10 /* A +/- count on the pages to pre-render */
+#define LOOK_AHEAD 1 /* A +/- count on the pages to pre-render */
#define MIN_SCALE 0.5
#define MAX_SCALE 4
#define MARGIN_BUFF 400
#define MAX_SEARCH 500
+#define SCALE_THUMB 0.25
static float screenScale = 1;
static fz_context *ctx = NULL;
+fz_document *m_doc;
+
using namespace winapp;
@@ -41,6 +44,37 @@ typedef struct win_stream_struct_s
} win_stream_struct;
static win_stream_struct win_stream;
+#ifndef NDEBUG
+unsigned int _mainThreadId = 0U;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ // The IsMainThread function returns true if the current thread is the app's main thread and false otherwise.
+ bool IsMainThread()
+ {
+ return (_mainThreadId == 0U || _mainThreadId == GetCurrentThreadId());
+ }
+
+ // The IsBackgroundThread function returns false if the current thread is the app's main thread and true otherwise.
+ bool IsBackgroundThread()
+ {
+ return (_mainThreadId == 0U || _mainThreadId != GetCurrentThreadId());
+ }
+
+ // The RecordMainThread function registers the main thread ID for use by the IsMainThread and IsBackgroundThread functions.
+ void RecordMainThread()
+ {
+ _mainThreadId = GetCurrentThreadId();
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not NDEBUG */
+
MainPage::MainPage()
{
InitializeComponent();
@@ -56,11 +90,25 @@ MainPage::MainPage()
m_renderedImage = ref new ImageBrush();
m_doc = NULL;
CleanUp();
+ RecordMainThread();
// use at most 128M for resource cache
ctx = fz_new_context(NULL, NULL, 128<<20);
}
+void run_async_non_interactive(std::function<void ()>&& action)
+{
+ Windows::UI::Core::CoreWindow^ wnd = Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow;
+ assert(wnd != nullptr);
+
+ wnd->Dispatcher->RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::Low,
+ ref new Windows::UI::Core::DispatchedHandler([action]()
+ {
+ action();
+ }));
+}
+
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
@@ -146,7 +194,17 @@ static RectSize fitPageToScreen(RectSize page, RectSize screen)
return pageSize;
}
-void MainPage::Prepare_bmp(int width, int height, DataWriter ^dw)
+spatial_info_t MainPage::InitSpatial(double scale)
+{
+ spatial_info_t value;
+
+ value.size.height = this->ActualHeight;
+ value.size.width = this->ActualWidth;
+ value.scale_factor = scale;
+ return value;
+}
+
+void Prepare_bmp(int width, int height, DataWriter ^dw)
{
int row_size = width * 4;
int bmp_size = row_size * height + 54;
@@ -170,6 +228,27 @@ void MainPage::Prepare_bmp(int width, int height, DataWriter ^dw)
dw->WriteInt32(0);
}
+void MainPage::InitThumbnails()
+{
+ this->m_thumbnails.raster = ref new Array<InMemoryRandomAccessStream^>(m_num_pages);
+ this->m_thumbnails.scale = ref new Array<double>(m_num_pages);
+ this->m_thumbnails.canvas_h = ref new Array<Canvas^>(m_num_pages);
+ this->m_thumbnails.canvas_v = ref new Array<Canvas^>(m_num_pages);
+ this->m_thumbnails.size = ref new Array<Point>(m_num_pages);
+}
+
+/* Return this page from a full res image to the thumb image. This should only
+ be called after all thumbs have been rendered. */
+void MainPage::SetThumb(int page_num)
+{
+ FlipViewItem ^flipview_temp = (FlipViewItem^) xaml_vert_flipView->Items->GetAt(page_num);
+ flipview_temp->Content = this->m_thumbnails.canvas_v[page_num];
+ flipview_temp->Background = this->m_blankPage;
+ flipview_temp = (FlipViewItem^) xaml_horiz_flipView->Items->GetAt(page_num);
+ flipview_temp->Content = this->m_thumbnails.canvas_h[page_num];
+ flipview_temp->Background = this->m_blankPage;
+}
+
/* Add rendered page into flipview structure at location page_num */
void MainPage::AddPage(int page_num)
{
@@ -202,18 +281,25 @@ void MainPage::AddBlankPage(int page_num, FlipView^ flip_view)
flip_view->Items->Append(flipview_temp);
}
+/* Add rendered page into flipview structure at location page_num */
+void MainPage::AddThumbNail(int page_num, FlipView^ flip_view)
+{
+ FlipViewItem ^flipview_temp = ref new FlipViewItem();
+ flipview_temp->Content = this->m_renderedCanvas;
+ flip_view->Items->Append(flipview_temp);
+}
+
/* Create white image for us to use as place holder in large document for flip
view filling instead of the thumbnail image */
void MainPage::CreateBlank(int width, int height)
{
- Platform::Array<unsigned char>^ bmp_data =
- ref new Platform::Array<unsigned char>(height * 4 * width);
+ Array<unsigned char>^ bmp_data = ref new Array<unsigned char>(height * 4 * width);
/* Set up the memory stream */
WriteableBitmap ^bmp = ref new WriteableBitmap(width, height);
InMemoryRandomAccessStream ^ras = ref new InMemoryRandomAccessStream();
DataWriter ^dw = ref new DataWriter(ras->GetOutputStreamAt(0));
/* Go ahead and write our header data into the memory stream */
- this->Prepare_bmp(width, height, dw);
+ Prepare_bmp(width, height, dw);
/* Set the data to all white */
memset(bmp_data->Data, 255, height * 4 * width);
@@ -300,7 +386,7 @@ static void win_close_file(fz_context *ctx, void *state)
delete dataReader;
}
-void winapp::MainPage::PixToMemStream(fz_pixmap *pix, DataWriter ^dw, Platform::Array<unsigned char> ^arr)
+void PixToMemStream(fz_pixmap *pix, DataWriter ^dw, Platform::Array<unsigned char> ^arr)
{
unsigned char *samples = fz_pixmap_samples(ctx, pix);
int w = fz_pixmap_width(ctx, pix);
@@ -315,25 +401,26 @@ void winapp::MainPage::PixToMemStream(fz_pixmap *pix, DataWriter ^dw, Platform::
}
}
-void winapp::MainPage::PageSize(fz_document *doc, fz_page *page, int *width, int *height, double scale_factor)
+void PageSize(fz_document *doc, fz_page *page, int *width, int *height, spatial_info_t spatial_info)
{
RectSize pageSize;
RectSize scale;
RectSize screenSize;
- screenSize.height = this->ActualHeight;
- screenSize.width = this->ActualWidth;
+ screenSize.height = spatial_info.size.height;
+ screenSize.width = spatial_info.size.width;
screenSize.width *= screenScale;
screenSize.height *= screenScale;
pageSize = measurePage(doc, page);
scale = fitPageToScreen(pageSize, screenSize);
- *width = pageSize.width * scale.width * scale_factor;
- *height = pageSize.height * scale.height * scale_factor;
+ *width = pageSize.width * scale.width * spatial_info.scale_factor;
+ *height = pageSize.height * scale.height * spatial_info.scale_factor;
}
-void winapp::MainPage::RenderPage(fz_document *doc, fz_page *page, int *width, int *height, double scale_factor)
+InMemoryRandomAccessStream^ RenderBitMap(fz_document *doc, fz_page *page, int *width,
+ int *height, spatial_info_t spatial_info)
{
fz_matrix ctm, *pctm = &ctm;
fz_device *dev;
@@ -342,34 +429,34 @@ void winapp::MainPage::RenderPage(fz_document *doc, fz_page *page, int *width, i
RectSize scale;
RectSize screenSize;
int bmp_width, bmp_height;
- Canvas^ my_Canvas = ref new Canvas();
- screenSize.height = this->ActualHeight;
- screenSize.width = this->ActualWidth;
+ screenSize.height = spatial_info.size.height;
+ screenSize.width = spatial_info.size.width;
screenSize.width *= screenScale;
screenSize.height *= screenScale;
pageSize = measurePage(doc, page);
scale = fitPageToScreen(pageSize, screenSize);
- pctm = fz_scale(pctm, scale.width * scale_factor, scale.height * scale_factor);
- bmp_width = pageSize.width * scale.width * scale_factor;
- bmp_height = pageSize.height * scale.height * scale_factor;
+ pctm = fz_scale(pctm, scale.width * spatial_info.scale_factor, scale.height * spatial_info.scale_factor);
+ bmp_width = pageSize.width * scale.width * spatial_info.scale_factor;
+ bmp_height = pageSize.height * scale.height * spatial_info.scale_factor;
+ *width = bmp_width;
+ *height = bmp_height;
/* Y is flipped for some reason */
ctm.f = bmp_height;
ctm.d = -ctm.d;
/* Allocate space for bmp */
- Platform::Array<unsigned char>^ bmp_data =
- ref new Platform::Array<unsigned char>(bmp_height * 4 * bmp_width);
- m_memory_use += bmp_height * 4 * bmp_width;
+ Array<unsigned char>^ bmp_data =
+ ref new Array<unsigned char>(bmp_height * 4 * bmp_width);
/* Set up the memory stream */
- WriteableBitmap ^bmp = ref new WriteableBitmap(bmp_width, bmp_height);
InMemoryRandomAccessStream ^ras = ref new InMemoryRandomAccessStream();
DataWriter ^dw = ref new DataWriter(ras->GetOutputStreamAt(0));
+ //m_memory_use += bmp_height * 4 * bmp_width;
/* Go ahead and write our header data into the memory stream */
- this->Prepare_bmp(bmp_width, bmp_height, dw);
+ Prepare_bmp(bmp_width, bmp_height, dw);
/* Now get a pointer to our samples and pass it to fitz to use */
pix = fz_new_pixmap_with_data(ctx, fz_device_bgr, bmp_width, bmp_height, &(bmp_data[0]));
fz_clear_pixmap_with_value(ctx, pix, 255);
@@ -378,17 +465,30 @@ void winapp::MainPage::RenderPage(fz_document *doc, fz_page *page, int *width, i
fz_free_device(dev);
/* Now the data into the memory stream */
PixToMemStream(pix, dw, bmp_data);
- /* And store in a new image brush */
+ /* Return raster stream */
+ return ras;
+}
+
+Canvas^ RenderPage(fz_document *doc, fz_page *page, int *width, int *height,
+ spatial_info_t spatial_info, ImageBrush^ *renderedImage)
+{
+ InMemoryRandomAccessStream^ ras;
+
+ /* Get raster bitmap stream */
+ ras = RenderBitMap(doc, page, width, height, spatial_info);
+
+ /* And store in a new image brush. Note: creation of WriteableBitmap
+ MUST be done by the UI thread. */
+ WriteableBitmap ^bmp = ref new WriteableBitmap(*width, *height);
bmp->SetSource(ras);
- m_renderedImage = ref new ImageBrush();
- m_renderedImage->Stretch = Windows::UI::Xaml::Media::Stretch::None;
- m_renderedImage->ImageSource = bmp;
- *width = bmp_width;
- *height = bmp_height;
- m_renderedCanvas = ref new Canvas();
- m_renderedCanvas->Height = bmp_height;
- m_renderedCanvas->Width = bmp_width;
- m_renderedCanvas->Background = this->m_renderedImage;
+ *renderedImage = ref new ImageBrush();
+ (*renderedImage)->Stretch = Windows::UI::Xaml::Media::Stretch::None;
+ (*renderedImage)->ImageSource = bmp;
+ Canvas^ ret_Canvas = ref new Canvas();
+ ret_Canvas->Height = *height;
+ ret_Canvas->Width = *width;
+ ret_Canvas->Background = *renderedImage;
+ return ret_Canvas;
}
void winapp::MainPage::SetupZoomCanvas()
@@ -419,7 +519,7 @@ void winapp::MainPage::CleanUp()
xaml_horiz_flipView->Items->Clear();
/* Clean up mupdf */
- if (this->m_doc != NULL)
+ if (m_doc != NULL)
fz_close_document(m_doc);
this->m_curr_flipView = nullptr;
@@ -434,11 +534,15 @@ void winapp::MainPage::CleanUp()
m_from_doubleflip = false;
m_first_time = false;
m_insearch = false;
+ m_search_active = false;
m_sliderchange = false;
m_flip_from_search = false;
m_num_pages = -1;
m_search_rect_count = 0;
ResetSearch();
+ m_ren_status = REN_AVAILABLE;
+ m_thumb_page_start = 0;
+ m_thumb_page_stop = 0;
m_curr_zoom = 1.0;
m_canvas_translate.X = 0;
@@ -446,7 +550,107 @@ void winapp::MainPage::CleanUp()
this->xaml_PageSlider->Minimum = m_slider_min;
this->xaml_PageSlider->Maximum = m_slider_max;
- this->xaml_PageSlider->IsEnabled = false;
+ this->xaml_PageSlider->IsEnabled = false;
+}
+
+/* Create the thumbnail images. This is started when we have space
+ on the render thread */
+void winapp::MainPage::RenderThumbs()
+{
+ spatial_info_t spatial_info = this->InitSpatial(1);
+ int num_pages = this->m_num_pages;
+ int thumb_pages = this->m_thumb_page_start;
+ int max_display =
+ max(spatial_info.size.height, spatial_info.size.width) * SCALE_THUMB;
+ cancellation_token_source cts;
+ auto token = cts.get_token();
+ m_ThumbCancel = cts;
+
+ this->m_ren_status = REN_THUMBS;
+ thumbs_t thumbnails = m_thumbnails;
+
+ create_task([spatial_info, max_display, num_pages, thumb_pages, thumbnails, this]()-> int
+ {
+ spatial_info_t spatial_info_local = spatial_info;
+ InMemoryRandomAccessStream ^ras = ref new InMemoryRandomAccessStream();
+
+ for (int k = thumb_pages; k < num_pages; k++)
+ {
+ int width, height;
+ int max_page_size;
+ double scale_factor;
+
+ fz_page *page = fz_load_page(m_doc, k);
+ // Get page size
+ spatial_info_local.scale_factor = 1;
+ PageSize(m_doc, page, &width, &height, spatial_info_local);
+ // Determine thumb scale factor
+ max_page_size = max(width, height);
+ scale_factor = (double) max_display/ (double) max_page_size;
+ spatial_info_local.scale_factor = 0.1;
+ thumbnails.raster[k] = RenderBitMap(m_doc, page, &width, &height,
+ spatial_info_local);
+ thumbnails.scale[k] = 0.1;
+ thumbnails.size[k].Y = height;
+ thumbnails.size[k].X = width;
+ if (is_task_cancellation_requested())
+ {
+ /* Just return the pages that we have done so far.*/
+ this->m_thumb_page_stop = k + 1;
+ cancel_current_task();
+ }
+ }
+ return num_pages; /* all done with thumbnails! */
+ }, token).then([this](task<int> the_task)
+ {
+ int new_end;
+ assert(IsMainThread);
+
+ try
+ {
+ new_end = the_task.get();
+ }
+ catch (const task_canceled& e)
+ {
+ new_end = this->m_thumb_page_stop;
+ }
+
+ int old_end = this->m_thumb_page_start;
+
+ /* Now go ahead and create the proper stuctures */
+ this->m_ren_status = REN_UPDATE_THUMB_CANVAS;
+ this->m_thumb_page_start = new_end;
+
+ for (int k = old_end; k < new_end; k++)
+ {
+ /* See if we already have something here as the main thread
+ may have already put in place the full scale image. Since this
+ operation is done on the main thread we should be safe here
+ from race conditions. Creation of bmp has to be done in ui thread */
+ FlipViewItem ^flipview_temp_v = (FlipViewItem^) xaml_vert_flipView->Items->GetAt(k);
+ FlipViewItem ^flipview_temp_h = (FlipViewItem^) xaml_horiz_flipView->Items->GetAt(k);
+ if (flipview_temp_h->Background != nullptr)
+ {
+ WriteableBitmap ^bmp = ref new WriteableBitmap(m_thumbnails.size[k].Y, m_thumbnails.size[k].X);
+ bmp->SetSource(m_thumbnails.raster[k]);
+ ImageBrush^ renderedImage = ref new ImageBrush();
+ renderedImage->Stretch = Windows::UI::Xaml::Media::Stretch::Fill;
+ renderedImage->ImageSource = bmp;
+ /* Different flip view items cannot share the same canvas */
+ m_thumbnails.canvas_h[k] = ref new Canvas();
+ m_thumbnails.canvas_h[k]->Height = m_thumbnails.size[k].Y / m_thumbnails.scale[k];
+ m_thumbnails.canvas_h[k]->Width = m_thumbnails.size[k].X / m_thumbnails.scale[k];
+ m_thumbnails.canvas_h[k]->Background = renderedImage;
+ m_thumbnails.canvas_v[k] = ref new Canvas();
+ m_thumbnails.canvas_v[k]->Height = m_thumbnails.size[k].Y / m_thumbnails.scale[k];
+ m_thumbnails.canvas_v[k]->Width = m_thumbnails.size[k].X / m_thumbnails.scale[k];
+ m_thumbnails.canvas_v[k]->Background = renderedImage;
+ flipview_temp_h->Content = m_thumbnails.canvas_h[k];
+ flipview_temp_v->Content = m_thumbnails.canvas_v[k];
+ }
+ }
+ this->m_ren_status = REN_AVAILABLE;
+ }, task_continuation_context::use_current());
}
void winapp::MainPage::OpenDocument(StorageFile^ file)
@@ -459,12 +663,33 @@ void winapp::MainPage::OpenDocument(StorageFile^ file)
WideCharToMultiByte(CP_UTF8, 0, w ,-1 ,name ,cb ,nullptr, nullptr);
char *ext = strrchr(name, '.');
- if (this->m_num_pages != -1)
- CleanUp();
+ if (this->m_num_pages != -1)
+ {
+ /* If the thumbnail thread is running then we need to end that first */
+ RenderingStatus_t *ren_status = &m_ren_status;
+ cancellation_token_source *ThumbCancel = &m_ThumbCancel;
+
+ /* Create a task to wait until the renderer is available */
+ auto t = create_task([ren_status, ThumbCancel]()->int
+ {
+ if (*ren_status == REN_THUMBS) {
+ ThumbCancel->cancel();
+ while (*ren_status == REN_THUMBS) {
+ }
+ }
+ return 0;
+ });
+
+ t.then([this](task<int> the_task)
+ {
+ CleanUp();
+ }, task_continuation_context::use_current());
+ }
this->SetupZoomCanvas();
+ auto ui = task_continuation_context::use_current();
- create_task(file->OpenAsync(FileAccessMode::Read)).then([this, file, ext](task<IRandomAccessStream^> task)
+ create_task(file->OpenAsync(FileAccessMode::Read)).then([this, file, ext, ui](task<IRandomAccessStream^> task)
{
try
{
@@ -482,14 +707,9 @@ void winapp::MainPage::OpenDocument(StorageFile^ file)
str->state = reinterpret_cast <void*> (&win_stream);
/* Now lets see if we can render the file */
-
m_doc = fz_open_document_with_stream(ctx, ext, str);
m_num_pages = m_doc->count_pages(m_doc);
- /* Set up the search progress bar */
- ProgressBar^ xaml_Progress = (ProgressBar^) (this->FindName("xaml_Progress"));
- xaml_Progress->Maximum = m_num_pages;
-
if ((m_currpage) >= m_num_pages)
{
m_currpage = m_num_pages - 1;
@@ -499,36 +719,36 @@ void winapp::MainPage::OpenDocument(StorageFile^ file)
m_currpage = 0;
}
- /* Set the other one up with the blanks */
+ /* Set up both flip views and intialize with blank pages */
FlipView^ temp_flip;
if (this->m_curr_flipView == xaml_vert_flipView)
temp_flip = xaml_horiz_flipView;
else
temp_flip = xaml_vert_flipView;
- /* Do a few pages */
+ /* Initialize all the flipvew items */
+ for (int k = 0; k < m_num_pages; k++)
+ {
+ AddBlankPage(k, xaml_horiz_flipView);
+ AddBlankPage(k, xaml_vert_flipView);
+ }
+ /* Do the current page now though */
int height, width;
+ spatial_info_t spatial_info = InitSpatial(1);
+
for (int k = 0; k < LOOK_AHEAD + 2; k++)
{
if (m_num_pages > k )
{
fz_page *page = fz_load_page(m_doc, k);
- this->RenderPage(m_doc, page, &width, &height, 1);
- AddPage(k);
+ this->m_renderedCanvas = RenderPage(m_doc, page, &width,
+ &height, spatial_info,
+ &m_renderedImage);
+ ReplacePage(k);
fz_free_page(m_doc, page);
- AddBlankPage(k, temp_flip);
- }
- }
- /* If we still have more pages, then set the rest to a blank white
- page which will get bumped as we move through the doc. */
- if (m_num_pages > LOOK_AHEAD + 2)
- {
- for (int k = LOOK_AHEAD + 2; k < m_num_pages; k++)
- {
- AddBlankPage(k);
- AddBlankPage(k, temp_flip);
}
}
+
/* Update the slider settings, if more than one page */
if (m_num_pages > 1)
{
@@ -552,30 +772,63 @@ void winapp::MainPage::OpenDocument(StorageFile^ file)
catch(COMException^ ex) {
this->HandleFileNotFoundException(ex);
}
+ }).then([this, ui]()
+ {
+ InitThumbnails();
+ this->RenderThumbs();
});
}
-void winapp::MainPage::RenderRange(int curr_page, int *height, int *width)
+task<int> winapp::MainPage::RenderRange(int curr_page, int *height, int *width)
{
/* Render +/- the look ahead from where we are if blank page is present */
- for (int k = curr_page - LOOK_AHEAD; k <= curr_page + LOOK_AHEAD; k++)
+ spatial_info_t spatial_info = InitSpatial(1);
+
+ RenderingStatus_t *ren_status = &m_ren_status;
+ cancellation_token_source *ThumbCancel = &m_ThumbCancel;
+
+ /* Create a task to wait until the renderer is available */
+ auto t = create_task([ren_status, ThumbCancel]()
+ {
+ if (*ren_status == REN_THUMBS) {
+ ThumbCancel->cancel();
+ while (*ren_status == REN_THUMBS) {
+ }
+ }
+ });
+
+ return t.then([this, height, width, curr_page, spatial_info]()
{
- if (k >= 0 && k < m_num_pages)
+ // assert(IsMainThread);
+ int val = 0;
+ /* This runs on the main ui thread */
+ for (int k = curr_page - LOOK_AHEAD; k <= curr_page + LOOK_AHEAD; k++)
{
- FlipViewItem ^flipview_temp = (FlipViewItem^) m_curr_flipView->Items->GetAt(k);
- if (flipview_temp->Background == this->m_blankPage)
+ if (k >= 0 && k < m_num_pages)
{
- fz_page *page = fz_load_page(m_doc, k);
- this->RenderPage(m_doc, page, width, height, 1);
- ReplacePage(k);
- fz_free_page(m_doc, page);
- }
- }
- }
- RectSize rectsize = this->currPageSize(curr_page);
- *height = rectsize.height;
- *width = rectsize.width;
- m_currpage = curr_page;
+ FlipViewItem ^flipview_temp = (FlipViewItem^) m_curr_flipView->Items->GetAt(k);
+ if (flipview_temp->Background == this->m_blankPage)
+ {
+ fz_page *page = fz_load_page(m_doc, k);
+ this->m_ren_status = REN_PAGE;
+ m_renderedCanvas = RenderPage(m_doc, page, width, height,
+ spatial_info, &m_renderedImage);
+ ReplacePage(k);
+ fz_free_page(m_doc, page);
+ this->m_ren_status = REN_AVAILABLE;
+ }
+ }
+ }
+ RectSize rectsize = this->currPageSize(curr_page);
+ *height = rectsize.height;
+ *width = rectsize.width;
+ m_currpage = curr_page;
+
+ /* Check if thumb rendering is done. If not then restart */
+ if (this->m_num_pages != this->m_thumb_page_start)
+ this->RenderThumbs();
+ return val;
+ }, task_continuation_context::use_current());
}
void winapp::MainPage::Slider_Released(Platform::Object^ sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e)
@@ -583,34 +836,51 @@ void winapp::MainPage::Slider_Released(Platform::Object^ sender, Windows::UI::Xa
int height, width;
int newValue = (int) this->xaml_PageSlider->Value - 1; /* zero based */
- this->RenderRange(newValue, &height, &width);
- this->m_currpage = newValue;
+ // this->RenderRange(newValue, &height, &width);
}
void winapp::MainPage::Slider_ValueChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e)
{
int newValue = (int) this->xaml_PageSlider->Value - 1; /* zero based */
+ RenderingStatus_t *ren_status = &m_ren_status;
+ cancellation_token_source *ThumbCancel = &m_ThumbCancel;
+ auto ui = task_continuation_context::use_current();
if (m_update_flip)
{
m_update_flip = false;
return;
}
+
if (m_init_done && this->xaml_PageSlider->IsEnabled)
{
FlipViewItem ^flipview_temp = (FlipViewItem^) m_curr_flipView->Items->GetAt(newValue);
if (flipview_temp->Background == this->m_blankPage)
{
- int width, height;
- fz_page *page = fz_load_page(m_doc, newValue);
- this->RenderPage(m_doc, page, &width, &height, 1);
- ReplacePage(newValue);
- this->m_currpage = newValue;
- fz_free_page(m_doc, page);
- }
- m_sliderchange = true;
- this->m_curr_flipView->SelectedIndex = newValue;
- ResetSearch();
+ create_task([ren_status, ThumbCancel]()
+ {
+ if (*ren_status == REN_THUMBS) {
+ ThumbCancel->cancel();
+ while (*ren_status == REN_THUMBS) {
+ }
+ }
+ }).then([this, newValue]()
+ {
+ int width, height;
+ fz_page *page = fz_load_page(m_doc, newValue);
+ spatial_info_t spatial_info = InitSpatial(1);
+ this->m_ren_status = REN_PAGE;
+ m_renderedCanvas = RenderPage(m_doc, page, &width, &height, spatial_info,
+ &m_renderedImage);
+ ReplacePage(newValue);
+ this->m_ren_status = REN_AVAILABLE;
+ this->m_currpage = newValue;
+ fz_free_page(m_doc, page);
+ m_sliderchange = true;
+ this->m_curr_flipView->SelectedIndex = newValue;
+ ResetSearch();
+ }, ui);
+ }
}
}
@@ -640,8 +910,30 @@ void winapp::MainPage::FlipView_SelectionChanged(Object^ sender, SelectionChange
{
ResetSearch();
}
- if (m_init_done)
- this->RenderRange(pos, &height, &width);
+ if (m_init_done)
+ {
+ /* Get the current page */
+ int curr_page = this->m_currpage;
+ task<int> task = this->RenderRange(pos, &height, &width);
+ task.then([this, curr_page, pos](int val)
+ {
+ /* To keep from having memory issue reset the page back to
+ the thumb if we are done rendering the thumbnails */
+ if (this->m_thumb_page_start == this->m_num_pages)
+ {
+ for (int k = curr_page - LOOK_AHEAD; k <= curr_page + LOOK_AHEAD; k++)
+ {
+ if (k < pos - LOOK_AHEAD || k > pos + LOOK_AHEAD)
+ {
+ if (k >= 0 && k < this->m_num_pages)
+ {
+ SetThumb(k);
+ }
+ }
+ }
+ }
+ }, task_continuation_context::use_current());
+ }
}
}
@@ -665,8 +957,10 @@ void winapp::MainPage::Canvas_ManipulationCompleted(Platform::Object^ sender, Wi
int width, height;
int pos = this->m_curr_flipView->SelectedIndex;
fz_page *page = fz_load_page(m_doc, pos);
+ spatial_info_t spatial_info = InitSpatial(m_curr_zoom);
- RenderPage(m_doc, page, &width, &height, m_curr_zoom);
+ m_renderedCanvas = RenderPage(m_doc, page, &width, &height, spatial_info,
+ &m_renderedImage);
this->xaml_zoomCanvas->Background = this->m_renderedImage;
m_renderedImage->Stretch = Windows::UI::Xaml::Media::Stretch::None;
@@ -685,18 +979,21 @@ void winapp::MainPage::Canvas_ManipulationDelta(Object^ sender, ManipulationDelt
/* Render at scaled resolution */
int pos = this->m_curr_flipView->SelectedIndex;
fz_page *page = fz_load_page(m_doc, pos);
+ spatial_info_t spatial_info = InitSpatial(m_curr_zoom);
+
m_curr_zoom = m_curr_zoom * e->Delta.Scale;
if (m_curr_zoom < MIN_SCALE) m_curr_zoom = MIN_SCALE;
if (m_curr_zoom > MAX_SCALE) m_curr_zoom = MAX_SCALE;
if (m_first_time)
{
- RenderPage(m_doc, page, &width, &height, m_curr_zoom);
+ m_renderedCanvas = RenderPage(m_doc, page, &width, &height, spatial_info,
+ &m_renderedImage);
this->xaml_zoomCanvas->Background = this->m_renderedImage;
m_renderedImage->Stretch = Windows::UI::Xaml::Media::Stretch::None;
}
else
{
- PageSize(m_doc, page, &width, &height, m_curr_zoom);
+ PageSize(m_doc, page, &width, &height, spatial_info);
m_renderedImage->Stretch = Windows::UI::Xaml::Media::Stretch::Fill;
}
this->xaml_zoomCanvas->Width = width;
@@ -742,10 +1039,12 @@ void winapp::MainPage::FlipView_Double(Object^ sender, DoubleTappedRoutedEventAr
int pos = this->m_curr_flipView->SelectedIndex;
int width, height;
fz_page *page = fz_load_page(m_doc, pos);
+ spatial_info_t spatial_info = InitSpatial(1);
- RenderPage(m_doc, page, &width, &height, 1);
- this->xaml_zoomCanvas->Background = this->m_renderedImage;
+ m_renderedCanvas = RenderPage(m_doc, page, &width, &height, spatial_info,
+ &m_renderedImage);
m_renderedImage->Stretch = Windows::UI::Xaml::Media::Stretch::None;
+ this->xaml_zoomCanvas->Background = m_renderedImage;
this->xaml_zoomCanvas->Width = width;
this->xaml_zoomCanvas->Height = height;
@@ -850,53 +1149,53 @@ void winapp::MainPage::Searcher(Platform::Object^ sender, Windows::UI::Xaml::Rou
void winapp::MainPage::ShowSearchResults(SearchResult_t result)
{
int height, width;
- this->RenderRange(result.page_num, &height, &width);
- RectSize screenSize;
- RectSize pageSize;
- RectSize scale;
- fz_page *page = fz_load_page(m_doc, result.page_num);
- FlipViewItem ^flipview_temp = (FlipViewItem^) m_curr_flipView->Items->GetAt(result.page_num);
- Canvas^ results_Canvas = (Canvas^) (flipview_temp->Content);
- // wchar_t buf[20];
+ task<int> task = this->RenderRange(result.page_num, &height, &width);
- m_searchpage = result.page_num;
+ /* Once the rendering is done launch this task to show the result */
+ task.then([this, result](int val)
+ {
+ RectSize screenSize;
+ RectSize pageSize;
+ RectSize scale;
+
+ fz_page *page = fz_load_page(m_doc, result.page_num);
+ FlipViewItem ^flipview_temp = (FlipViewItem^) m_curr_flipView->Items->GetAt(result.page_num);
+ Canvas^ results_Canvas = (Canvas^) (flipview_temp->Content);
- screenSize.height = this->ActualHeight;
- screenSize.width = this->ActualWidth;
+ m_searchpage = result.page_num;
- screenSize.width *= screenScale;
- screenSize.height *= screenScale;
+ screenSize.height = this->ActualHeight;
+ screenSize.width = this->ActualWidth;
+
+ screenSize.width *= screenScale;
+ screenSize.height *= screenScale;
- pageSize = measurePage(m_doc, page);
- scale = fitPageToScreen(pageSize, screenSize);
+ pageSize = measurePage(m_doc, page);
+ scale = fitPageToScreen(pageSize, screenSize);
- /* Now add the rects */
- for (int k = 0; k < result.box_count && k < MAX_SEARCH; k++)
- {
- /* Create a new ref counted Rectangle */
- Rectangle^ a_rectangle = ref new Rectangle();
- TranslateTransform ^trans_transform = ref new TranslateTransform();
- a_rectangle->Width = hit_bbox[k].x1 - hit_bbox[k].x0;
- a_rectangle->Height = hit_bbox[k].y1 - hit_bbox[k].y0;
- trans_transform->X = hit_bbox[k].x0 * scale.width;
- trans_transform->Y = hit_bbox[k].y0 * scale.height;
- a_rectangle->Width *= scale.width;
- a_rectangle->Height *= scale.height;
- /* Give it a unique name */
-#if 0
- int len = swprintf_s(buf, 20, L"%s_%d", L"Rect",k );
- a_rectangle->Name = ref new String(buf);
-#endif
- a_rectangle->RenderTransform = trans_transform;
- a_rectangle->Fill = m_color_brush;
- results_Canvas->Children->Append(a_rectangle);
- m_search_rect_count += 1;
- }
- if (result.box_count > 0)
- {
- m_flip_from_search = true;
- this->m_curr_flipView->SelectedIndex = result.page_num;
- }
+ /* Now add the rects */
+ for (int k = 0; k < result.box_count && k < MAX_SEARCH; k++)
+ {
+ /* Create a new ref counted Rectangle */
+ Rectangle^ a_rectangle = ref new Rectangle();
+ TranslateTransform ^trans_transform = ref new TranslateTransform();
+ a_rectangle->Width = hit_bbox[k].x1 - hit_bbox[k].x0;
+ a_rectangle->Height = hit_bbox[k].y1 - hit_bbox[k].y0;
+ trans_transform->X = hit_bbox[k].x0 * scale.width;
+ trans_transform->Y = hit_bbox[k].y0 * scale.height;
+ a_rectangle->Width *= scale.width;
+ a_rectangle->Height *= scale.height;
+ a_rectangle->RenderTransform = trans_transform;
+ a_rectangle->Fill = m_color_brush;
+ results_Canvas->Children->Append(a_rectangle);
+ m_search_rect_count += 1;
+ }
+ if (result.box_count > 0)
+ {
+ m_flip_from_search = true;
+ this->m_curr_flipView->SelectedIndex = result.page_num;
+ }
+ }, task_continuation_context::use_current());
}
void winapp::MainPage::SearchNext(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
@@ -904,8 +1203,22 @@ void winapp::MainPage::SearchNext(Platform::Object^ sender, Windows::UI::Xaml::R
StackPanel^ leftPanel = (StackPanel^) this->TopAppBar->FindName("LeftPanel");
TextBox^ findBox = (TextBox^) leftPanel->FindName("findBox");
String^ textToFind = findBox->Text;
+ RenderingStatus_t *ren_status = &m_ren_status;
+ cancellation_token_source *ThumbCancel = &m_ThumbCancel;
- SearchInDirection(1, textToFind);
+ /* Create a task to wait until the renderer is available */
+ create_task([ren_status, ThumbCancel]()
+ {
+ if (*ren_status == REN_THUMBS) {
+ ThumbCancel->cancel();
+ while (*ren_status == REN_THUMBS) {
+ }
+ }
+ }).then([this, textToFind]()
+ {
+ if (this->m_search_active == false)
+ SearchInDirection(1, textToFind);
+ }, task_continuation_context::use_current());
}
void winapp::MainPage::SearchPrev(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
@@ -913,8 +1226,22 @@ void winapp::MainPage::SearchPrev(Platform::Object^ sender, Windows::UI::Xaml::R
StackPanel^ leftPanel = (StackPanel^) this->TopAppBar->FindName("LeftPanel");
TextBox^ findBox = (TextBox^) leftPanel->FindName("findBox");
String^ textToFind = findBox->Text;
+ RenderingStatus_t *ren_status = &m_ren_status;
+ cancellation_token_source *ThumbCancel = &m_ThumbCancel;
- SearchInDirection(-1, textToFind);
+ /* Create a task to wait until the renderer is available */
+ create_task([ren_status, ThumbCancel]()
+ {
+ if (*ren_status == REN_THUMBS) {
+ ThumbCancel->cancel();
+ while (*ren_status == REN_THUMBS) {
+ }
+ }
+ }).then([this, textToFind]()
+ {
+ if (this->m_search_active == false)
+ SearchInDirection(-1, textToFind);
+ }, task_continuation_context::use_current());
}
void winapp::MainPage::CancelSearch(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
@@ -952,7 +1279,10 @@ void winapp::MainPage::SearchInDirection(int dir, String^ textToFind)
int cb = WideCharToMultiByte(CP_UTF8, 0, textToFind->Data(), -1, nullptr, 0, nullptr, nullptr);
char* needle = new char[cb];
fz_document *local_doc = m_doc;
- auto cancel_token = m_searchcts.get_token(); /* Cancelation token */
+
+ cancellation_token_source cts;
+ auto token = cts.get_token();
+ m_searchcts = cts;
SearchResult_t result;
int pos = m_currpage;
@@ -988,10 +1318,9 @@ void winapp::MainPage::SearchInDirection(int dir, String^ textToFind)
{
my_bar->Value = start;
} */
-
-
/* Get the ui thread */
auto ui = task_continuation_context::use_current();
+ this->m_search_active = true;
/* Do task lambdas here to avoid UI blocking issues */
auto search_task = create_task([this, needle, dir, start, local_doc, &result]()->SearchResult_t
@@ -1015,8 +1344,7 @@ void winapp::MainPage::SearchInDirection(int dir, String^ textToFind)
/* Todo no matches found alert */
free(needle);
return result;
- }, cancel_token);
-
+ }, token);
/* Do the continuation on the ui thread */
search_task.then([this](task<SearchResult_t> the_task)
{
@@ -1026,7 +1354,8 @@ void winapp::MainPage::SearchInDirection(int dir, String^ textToFind)
// ProgressBar^ xaml_Progress = (ProgressBar^) (this->FindName("xaml_Progress"));
// xaml_Progress->IsEnabled = false;
// xaml_Progress->Opacity = 0.0;
- this->ShowSearchResults(the_result);
+ this->ShowSearchResults(the_result);
+ this->m_search_active = false;
}
}, ui);
}
@@ -1073,7 +1402,8 @@ void winapp::MainPage::GridSizeChanged()
xaml_vert_flipView->Opacity = 0;
}
UpDatePageSizes();
- if (old_flip != m_curr_flipView && old_flip != nullptr)
+
+ if (m_num_pages > 0 && old_flip != m_curr_flipView && old_flip != nullptr)
this->m_curr_flipView->SelectedIndex = this->m_currpage;
}
@@ -1081,20 +1411,27 @@ void winapp::MainPage::UpDatePageSizes()
{
int width, height;
- /* Render our current pages at the new resolution and mark the rest with the blank */
+ /* Render our current pages at the new resolution and rescale the thumbnail
+ canvas if needed */
if (m_num_pages > 0)
{
for (int i = 0; i < m_num_pages; i++)
{
FlipViewItem ^flipview_temp = (FlipViewItem^) m_curr_flipView->Items->GetAt(i);
- if (flipview_temp != nullptr)
+ if (flipview_temp != nullptr && flipview_temp->Content != nullptr)
{
- flipview_temp->Content = nullptr;
- flipview_temp->Background = this->m_blankPage;
+ Canvas^ curr_canvas = (Canvas^) flipview_temp->Content;
+ int curr_canvas_height = curr_canvas->Height;
+ int curr_canvas_width = curr_canvas->Width;
+
+ double scale_x = (double) curr_canvas_height / (double) this->xaml_zoomCanvas->Height;
+ double scale_y = (double) curr_canvas_width / (double) this->xaml_zoomCanvas->Width;
+
+ double min_scale = max(scale_x, scale_y);
+ curr_canvas->Height = curr_canvas_height / min_scale;
+ curr_canvas->Width = curr_canvas_width / min_scale;
}
- }
+ }
this->RenderRange(this->m_currpage, &height, &width);
}
};
-
-
diff --git a/winRT/winapp/MainPage.xaml.h b/winRT/winapp/MainPage.xaml.h
index fb7d4a5c..3678f4fc 100644
--- a/winRT/winapp/MainPage.xaml.h
+++ b/winRT/winapp/MainPage.xaml.h
@@ -23,7 +23,13 @@ using namespace Windows::Foundation;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Input;
-//using namespace winapp::DataBinding;
+
+typedef enum {
+ REN_AVAILABLE = 0,
+ REN_THUMBS,
+ REN_UPDATE_THUMB_CANVAS,
+ REN_PAGE /* Used to ignore value when source based setting */
+} RenderingStatus_t;
typedef struct SearchResult_s
{
@@ -37,6 +43,21 @@ typedef struct RectSize_s
float height;
} RectSize;
+typedef struct spatial_info_s
+{
+ RectSize size;
+ double scale_factor;
+} spatial_info_t;
+
+typedef struct thumbs_s
+{
+ Array<InMemoryRandomAccessStream^>^ raster;
+ Array<double>^ scale;
+ Array<Point>^ size;
+ Array<Canvas^>^ canvas_v;
+ Array<Canvas^>^ canvas_h;
+} thumbs_t;
+
namespace winapp
{
/// <summary>
@@ -64,10 +85,10 @@ namespace winapp
int m_search_rect_count;
Point m_display_size;
cancellation_token_source m_searchcts;
+ cancellation_token_source m_thumbcts;
long long m_memory_use;
double m_curr_zoom;
Point m_zoom_size;
- fz_document *m_doc;
Point m_touchpoint;
Point m_canvas_translate;
Windows::UI::Input::ManipulationDelta m_changes;
@@ -77,17 +98,22 @@ namespace winapp
ImageBrush^ m_zoomedImage;
SolidColorBrush^ m_color_brush;
FlipView^ m_curr_flipView;
+ thumbs_t m_thumbnails;
+ RenderingStatus_t m_ren_status;
+ int m_thumb_page_start;
+ int m_thumb_page_stop;
+ cancellation_token_source m_ThumbCancel;
bool m_zoom_mode;
bool m_from_doubleflip;
bool m_scaling_occured;
- bool m_insearch;
+ bool m_insearch; /* Used for UI display */
+ bool m_search_active; /* Used to avoid multiple UI clicks */
bool m_sliderchange;
bool m_update_flip;
void Picker(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void Searcher(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
void OpenDocument(StorageFile^ file);
- void RenderPage(fz_document *doc, fz_page *page, int *width, int *height, double scale);
- void RenderRange(int curr_page, int *height, int *width);
+ task<int> RenderRange(int curr_page, int *height, int *width);
void CleanUp();
void AddPage(int page_num);
void ReplacePage(int page_num);
@@ -98,8 +124,6 @@ namespace winapp
void NotifyUserFileNotExist();
void SetupZoomCanvas();
RectSize currPageSize(int page);
- void Prepare_bmp(int width, int height, DataWriter ^dw);
- void PixToMemStream(fz_pixmap *pix, DataWriter ^dw, Platform::Array<unsigned char> ^arr);
void Slider_ValueChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e);
void Slider_Released(Platform::Object^ sender, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e);
void FlipView_SelectionChanged(Object^ sender, SelectionChangedEventArgs^ e);
@@ -117,7 +141,11 @@ namespace winapp
void GridSizeChanged();
void UpDatePageSizes();
void ShowThumbnail();
- void PageSize(fz_document *doc, fz_page *page, int *width, int *height, double scale_factor);
void Canvas_ManipulationCompleted(Platform::Object^ sender, Windows::UI::Xaml::Input::ManipulationCompletedRoutedEventArgs^ e);
+ void AddThumbNail(int page_num, FlipView^ flip_view);
+ spatial_info_t InitSpatial(double scale);
+ void InitThumbnails();
+ void RenderThumbs();
+ void SetThumb(int page_num);
};
}