diff options
author | Michael Vrhel <michael.vrhel@artifex.com> | 2013-03-26 23:15:02 -0700 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2013-05-16 19:25:34 +0100 |
commit | aa0c80c1bd70cff8727e83996fc555461ef68278 (patch) | |
tree | 4dfd17f64fca38cc7c148aaef6926467684370ab | |
parent | 23575e94b917921484b824d4cbd920d386a8d971 (diff) | |
download | mupdf-aa0c80c1bd70cff8727e83996fc555461ef68278.tar.xz |
Fix a number of crashing issues dealing with create_task
-rw-r--r-- | winRT/winapp/MainPage.xaml | 3 | ||||
-rw-r--r-- | winRT/winapp/MainPage.xaml.cpp | 639 | ||||
-rw-r--r-- | winRT/winapp/MainPage.xaml.h | 44 |
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); }; } |