1 /***************************************************************************
2 ComicBook.cpp - ComicBook class and its children
3 -------------------
4 copyright : (C) 2003-2006 by James Athey
5 email : jathey@comcast.net
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * In addition, as a special exception, the author gives permission to *
16 * link the code of his release of Comical with Rarlabs' "unrar" *
17 * library (or with modified versions of it that use the same license *
18 * as the "unrar" library), and distribute the linked executables. You *
19 * must obey the GNU General Public License in all respects for all of *
20 * the code used other than "unrar". If you modify this file, you may *
21 * extend this exception to your version of the file, but you are not *
22 * obligated to do so. If you do not wish to do so, delete this *
23 * exception statement from your version. *
24 * *
25 ***************************************************************************/
26
27 #include "ComicBook.h"
28 #include "Exceptions.h"
29 #include <cstring>
30 #include <wx/datetime.h>
31 #include <wx/mstream.h>
32 #include <wx/utils.h>
33 #include <wx/textdlg.h>
34
35 DEFINE_EVENT_TYPE(EVT_PAGE_SCALED)
DEFINE_EVENT_TYPE(EVT_PAGE_THUMBNAILED)36 DEFINE_EVENT_TYPE(EVT_PAGE_THUMBNAILED)
37 DEFINE_EVENT_TYPE(EVT_CURRENT_PAGE_CHANGED)
38 DEFINE_EVENT_TYPE(EVT_PAGE_ERROR)
39
40 ComicBook::ComicBook(wxString _filename, wxUint32 _cacheLen, COMICAL_ZOOM _zoom, long _zoomLevel, bool _fitOnlyOversize, COMICAL_MODE _mode, FREE_IMAGE_FILTER _filter, COMICAL_DIRECTION _direction, wxInt32 _scrollbarThickness) : wxThread(wxTHREAD_JOINABLE), filename(_filename), cacheLen(_cacheLen), zoom(_zoom), zoomLevel(_zoomLevel), fitOnlyOversize(_fitOnlyOversize), mode(_mode), filter(_filter), direction(_direction), scrollbarThickness(_scrollbarThickness)
41 {
42 pageCount = 0;
43 currentPage = 0;
44 evtHandler = new wxEvtHandler();
45 // Each of the long values is followed by the letter L not the number one
46 Filenames = new wxArrayString();
47 originals = NULL;
48 resamples = NULL;
49 thumbnails = NULL;
50 Orientations = NULL;
51 originalLockers = NULL;
52 resampleLockers = NULL;
53 thumbnailLockers = NULL;
54 password = NULL;
55 }
56
~ComicBook()57 ComicBook::~ComicBook()
58 {
59 for(wxUint32 i = 0; i < pageCount; i++) {
60 if (originals[i].Ok())
61 originals[i].Destroy();
62 if (resamples[i].Ok())
63 resamples[i].Destroy();
64 if (thumbnails[i].Ok())
65 thumbnails[i].Destroy();
66 }
67 if (originals)
68 delete[] originals;
69 if (resamples)
70 delete[] resamples;
71 if (thumbnails)
72 delete[] thumbnails;
73 if (Orientations)
74 delete[] Orientations;
75 if (originalLockers)
76 delete[] originalLockers;
77 if (resampleLockers)
78 delete[] resampleLockers;
79 if (thumbnailLockers)
80 delete[] thumbnailLockers;
81 if (Filenames)
82 delete Filenames;
83 if (password)
84 delete[] password;
85 if (evtHandler)
86 delete evtHandler;
87 }
88
RotatePage(wxUint32 pagenumber,COMICAL_ROTATE direction)89 void ComicBook::RotatePage(wxUint32 pagenumber, COMICAL_ROTATE direction)
90 {
91 if (Orientations[pagenumber] != direction) {
92 wxMutexLocker rlock(resampleLockers[pagenumber]);
93 wxMutexLocker tlock(thumbnailLockers[pagenumber]);
94 Orientations[pagenumber] = direction;
95 resamples[pagenumber].Destroy();
96 thumbnails[pagenumber].Destroy();
97 }
98 }
99
100 /* returns TRUE if newMode is different, FALSE if the same and no changes were made */
SetMode(COMICAL_MODE newMode)101 bool ComicBook::SetMode(COMICAL_MODE newMode)
102 {
103 if(mode != newMode) {
104 wxUint32 i;
105 for (i = 0; i < pageCount; i++) {
106 resampleLockers[i].Lock();
107 if (resamples[i].Ok())
108 resamples[i].Destroy();
109 }
110 mode = newMode;
111 for (i = 0; i < pageCount; i++)
112 resampleLockers[i].Unlock();
113 return TRUE;
114 } else
115 return FALSE;
116 }
117
118 /* returns TRUE if newFilter is different, FALSE if the same and no changes were made */
SetFilter(FREE_IMAGE_FILTER newFilter)119 bool ComicBook::SetFilter(FREE_IMAGE_FILTER newFilter)
120 {
121 if(filter != newFilter) {
122 wxUint32 i;
123 for (i = 0; i < pageCount; i++) {
124 resampleLockers[i].Lock();
125 if (resamples[i].Ok())
126 resamples[i].Destroy();
127 }
128 filter = newFilter;
129 for (i = 0; i < pageCount; i++)
130 resampleLockers[i].Unlock();
131 return TRUE;
132 } else
133 return FALSE;
134 }
135
136 /* Makes pages zoom to newZoom level, and will zoom pages that are smaller than the canvas only if newFitOnlyIfOversize is FALSE.
137 * returns TRUE if parameters were different, FALSE if parameters were the same and no changes were made */
SetZoom(COMICAL_ZOOM newZoom)138 bool ComicBook::SetZoom(COMICAL_ZOOM newZoom)
139 {
140 if(zoom != newZoom) {
141 wxUint32 i;
142 for (i = 0; i < pageCount; i++) {
143 resampleLockers[i].Lock();
144 if (resamples[i].Ok())
145 resamples[i].Destroy();
146 }
147
148 zoom = newZoom;
149 for (i = 0; i < pageCount; i++)
150 resampleLockers[i].Unlock();
151 return TRUE;
152 } else
153 return FALSE;
154 }
155
156 /* Makes pages zoom to newZoomLevel%.
157 * returns TRUE if parameters were different, FALSE if parameters were the same and no changes were made */
SetZoomLevel(long newZoomLevel)158 bool ComicBook::SetZoomLevel(long newZoomLevel)
159 {
160 if(zoomLevel != newZoomLevel) {
161 wxUint32 i;
162 if (zoom == ZOOM_CUSTOM) {
163 for (i = 0; i < pageCount; i++) {
164 resampleLockers[i].Lock();
165 if (resamples[i].Ok())
166 resamples[i].Destroy();
167 }
168 }
169 zoomLevel = newZoomLevel;
170 if (zoom == ZOOM_CUSTOM) {
171 for (i = 0; i < pageCount; i++)
172 resampleLockers[i].Unlock();
173 }
174 return TRUE;
175 } else
176 return FALSE;
177 }
178
SetFitOnlyOversize(bool newFitOnlyOversize)179 bool ComicBook::SetFitOnlyOversize(bool newFitOnlyOversize)
180 {
181 if (fitOnlyOversize != newFitOnlyOversize) {
182 wxUint32 i;
183 for (i = 0; i < pageCount; i++) {
184 resampleLockers[i].Lock();
185 if (resamples[i].Ok())
186 resamples[i].Destroy();
187 }
188
189 fitOnlyOversize = newFitOnlyOversize;
190 for (i = 0; i < pageCount; i++)
191 resampleLockers[i].Unlock();
192 return TRUE;
193 } else
194 return FALSE;
195 }
196
197 /* returns TRUE if direction is different, FALSE if same and no changes were made */
SetDirection(COMICAL_DIRECTION newDirection)198 bool ComicBook::SetDirection(COMICAL_DIRECTION newDirection)
199 {
200 if(direction != newDirection) {
201 direction = newDirection;
202 return TRUE;
203 } else
204 return FALSE;
205 }
206
207 /* returns TRUE if parameters were different, FALSE if parameters were the same and no changes were made */
SetCanvasSize(wxSize canvasSize)208 bool ComicBook::SetCanvasSize(wxSize canvasSize)
209 {
210 if(canvasWidth != canvasSize.x || canvasHeight != canvasSize.y) {
211 wxUint32 i;
212 for (i = 0; i < pageCount; i++) {
213 resampleLockers[i].Lock();
214 if (resamples[i].Ok())
215 resamples[i].Destroy();
216 }
217 canvasWidth = canvasSize.x;
218 canvasHeight = canvasSize.y;
219 for (i = 0; i < pageCount; i++)
220 resampleLockers[i].Unlock();
221 return TRUE;
222 } else
223 return FALSE;
224 }
225
SetScrollbarThickness(wxInt32 newScrollbarThickness)226 bool ComicBook::SetScrollbarThickness(wxInt32 newScrollbarThickness)
227 {
228 if(scrollbarThickness != newScrollbarThickness) {
229 wxUint32 i;
230 for (i = 0; i < pageCount; i++) {
231 resampleLockers[i].Lock();
232 if (resamples[i].Ok())
233 resamples[i].Destroy();
234 }
235 scrollbarThickness = newScrollbarThickness;
236 for (i = 0; i < pageCount; i++)
237 resampleLockers[i].Unlock();
238 return TRUE;
239 } else
240 return FALSE;
241 }
242
GetPage(wxUint32 pagenumber)243 wxBitmap * ComicBook::GetPage(wxUint32 pagenumber)
244 {
245 if (pagenumber > pageCount)
246 throw new PageOutOfRangeException(pagenumber, pageCount);
247 wxMutexLocker lock(resampleLockers[pagenumber]);
248 if (!resamples[pagenumber].Ok())
249 return NULL;
250 return new wxBitmap(resamples[pagenumber]);
251 }
252
GetPageLeftHalf(wxUint32 pagenumber)253 wxBitmap * ComicBook::GetPageLeftHalf(wxUint32 pagenumber)
254 {
255 if (pagenumber > pageCount)
256 throw new PageOutOfRangeException(pagenumber, pageCount);
257 wxMutexLocker lock(resampleLockers[pagenumber]);
258 if (!resamples[pagenumber].Ok())
259 return NULL;
260 wxInt32 rWidth = resamples[pagenumber].GetWidth();
261 wxInt32 rHeight = resamples[pagenumber].GetHeight();
262 if (direction == COMICAL_RTL) {
263 wxInt32 offset = rWidth / 2;
264 wxInt32 remainder = rWidth % 2;
265 return new wxBitmap(resamples[pagenumber].GetSubImage(wxRect(offset, 0, offset + remainder, rHeight)));
266 } else
267 return new wxBitmap(resamples[pagenumber].GetSubImage(wxRect(0, 0, rWidth / 2, rHeight)));
268 }
269
GetPageRightHalf(wxUint32 pagenumber)270 wxBitmap * ComicBook::GetPageRightHalf(wxUint32 pagenumber)
271 {
272 if (pagenumber > pageCount)
273 throw new PageOutOfRangeException(pagenumber, pageCount);
274 wxMutexLocker lock(resampleLockers[pagenumber]);
275 if (!resamples[pagenumber].Ok())
276 return NULL;
277 wxInt32 rWidth = resamples[pagenumber].GetWidth();
278 wxInt32 rHeight = resamples[pagenumber].GetHeight();
279 if (direction == COMICAL_LTR) {
280 wxInt32 offset = rWidth / 2;
281 wxInt32 remainder = rWidth % 2;
282 return new wxBitmap(resamples[pagenumber].GetSubImage(wxRect(offset, 0, offset + remainder, rHeight)));
283 } else
284 return new wxBitmap(resamples[pagenumber].GetSubImage(wxRect(0, 0, rWidth / 2, rHeight)));
285 }
286
GetThumbnail(wxUint32 pagenumber)287 wxBitmap * ComicBook::GetThumbnail(wxUint32 pagenumber)
288 {
289 if (pagenumber > pageCount)
290 throw new PageOutOfRangeException(pagenumber, pageCount);
291 wxMutexLocker lock(thumbnailLockers[pagenumber]);
292 if (!thumbnails[pagenumber].Ok())
293 return NULL;
294 return new wxBitmap(thumbnails[pagenumber]);
295 }
296
IsPageLandscape(wxUint32 pagenumber)297 bool ComicBook::IsPageLandscape(wxUint32 pagenumber)
298 {
299 wxInt32 rWidth, rHeight;
300 if (pagenumber > pageCount)
301 throw new PageOutOfRangeException(pagenumber, pageCount);
302
303 resampleLockers[pagenumber].Lock();
304 if (resamples[pagenumber].Ok()) {
305 rWidth = resamples[pagenumber].GetWidth();
306 rHeight = resamples[pagenumber].GetHeight();
307 resampleLockers[pagenumber].Unlock();
308 goto IsPageLandscapeRatioCalculate;
309 }
310 resampleLockers[pagenumber].Unlock();
311
312 thumbnailLockers[pagenumber].Lock();
313 if (thumbnails[pagenumber].Ok()) {
314 rWidth = thumbnails[pagenumber].GetWidth();
315 rHeight = thumbnails[pagenumber].GetHeight();
316 thumbnailLockers[pagenumber].Unlock();
317 goto IsPageLandscapeRatioCalculate;
318 }
319 thumbnailLockers[pagenumber].Unlock();
320
321 wxBeginBusyCursor();
322
323 // If the page has never been loaded, even in thumbnail form, the display
324 // will really screw up if the page IS landscape and this function says
325 // it's not. Therefore, we wait until it is ready.
326 while (!originals[pagenumber].Ok()) {
327 wxMilliSleep(10);
328 }
329
330 originalLockers[pagenumber].Lock();
331 if (Orientations[pagenumber] == NORTH ||
332 Orientations[pagenumber] == SOUTH) {
333 rWidth = originals[pagenumber].GetWidth();
334 rHeight = originals[pagenumber].GetHeight();
335 } else {
336 rHeight = originals[pagenumber].GetWidth();
337 rWidth = originals[pagenumber].GetHeight();
338 }
339 originalLockers[pagenumber].Unlock();
340
341 wxEndBusyCursor();
342
343 IsPageLandscapeRatioCalculate:
344 if ((float(rWidth)/float(rHeight)) > 1.0f)
345 return true;
346 else
347 return false;
348 }
349
IsPageReady(wxUint32 pagenumber)350 bool ComicBook::IsPageReady(wxUint32 pagenumber)
351 {
352 if (pagenumber > pageCount)
353 throw new PageOutOfRangeException(pagenumber, pageCount);
354 return resamples[pagenumber].Ok();
355 }
356
IsThumbReady(wxUint32 pagenumber)357 bool ComicBook::IsThumbReady(wxUint32 pagenumber)
358 {
359 if (pagenumber > pageCount)
360 throw new PageOutOfRangeException(pagenumber, pageCount);
361 return thumbnails[pagenumber].Ok();
362 }
363
FitWithoutScrollbars(wxUint32 pagenumber,float * scalingFactor)364 bool ComicBook::FitWithoutScrollbars(wxUint32 pagenumber, float *scalingFactor)
365 {
366 wxSize size;
367 wxInt32 usableWidth, usableWidthScrollbar, usableHeightScrollbar, withoutScrollBarHeight, withoutScrollBarWidth;
368
369 if (pagenumber > pageCount)
370 throw new PageOutOfRangeException(pagenumber, pageCount);
371
372 if (Orientations[pagenumber] == NORTH || Orientations[pagenumber] == SOUTH)
373 size = wxSize(originals[pagenumber].GetWidth(), originals[pagenumber].GetHeight());
374 else
375 size = wxSize(originals[pagenumber].GetHeight(), originals[pagenumber].GetWidth());
376
377 float ratio = float (size.x) / float(size.y);
378
379 switch(zoom) {
380 case ZOOM_WIDTH:
381 if (ratio >= 1.0f || mode == ONEPAGE) {
382 usableWidth = canvasWidth;
383 usableWidthScrollbar = canvasWidth - scrollbarThickness;
384 } else {
385 usableWidth = canvasWidth / 2;
386 usableWidthScrollbar = (canvasWidth - scrollbarThickness) / 2;
387 }
388 *scalingFactor = float(usableWidth) / float(size.x);
389 withoutScrollBarHeight = wxInt32(float(size.y) * *scalingFactor);
390 if (withoutScrollBarHeight > canvasHeight) {
391 // it's possible the page will fit without scrollbars at some
392 // width in between canvasWidth - scrollbarThickness and
393 // canvasWidth.
394 // scaling factor if height is maximized
395 *scalingFactor = float(canvasHeight) / float(size.y);
396 // which will fit best?
397 if (wxInt32(*scalingFactor * float(size.x)) > usableWidthScrollbar)
398 return true;
399 else
400 return false;
401 } else
402 return true;
403 case ZOOM_HEIGHT:
404 if (ratio >= 1.0f || mode == ONEPAGE) {
405 usableWidth = canvasWidth;
406 } else {
407 usableWidth = canvasWidth / 2;
408 }
409 *scalingFactor = float(canvasHeight) / float(size.y);
410 withoutScrollBarWidth = wxInt32(float(size.x) * *scalingFactor);
411 if (withoutScrollBarWidth > usableWidth) {
412 // it's possible the page will fit without scrollbars at some
413 // width in between canvasHeight - scrollbarThickness and
414 // canvasHeight.
415 // scaling factor if width is maximized
416 *scalingFactor = float(usableWidth) / float(size.x);
417 // which will fit best?
418 usableHeightScrollbar = canvasHeight - scrollbarThickness;
419 if ((*scalingFactor * size.y) > usableHeightScrollbar)
420 return true;
421 else
422 return false;
423 } else
424 return true;
425 case ZOOM_FIT:
426 return true;
427 default:
428 // this function should not be called for other zooms
429 break;
430 }
431 return false;
432 }
433
FitWithoutScrollbars(wxUint32 pagenumber)434 bool ComicBook::FitWithoutScrollbars(wxUint32 pagenumber)
435 {
436 float throwaway = 0.0f;
437 return FitWithoutScrollbars(pagenumber, &throwaway);
438 }
439
IsOversize(wxUint32 pagenumber)440 bool ComicBook::IsOversize(wxUint32 pagenumber)
441 {
442 wxSize size;
443 wxInt32 usableWidth, usableHeight;
444
445 if (pagenumber > pageCount)
446 throw new PageOutOfRangeException(pagenumber, pageCount);
447
448 if (Orientations[pagenumber] == NORTH || Orientations[pagenumber] == SOUTH)
449 size = wxSize(originals[pagenumber].GetWidth(), originals[pagenumber].GetHeight());
450 else
451 size = wxSize(originals[pagenumber].GetHeight(), originals[pagenumber].GetWidth());
452
453 float ratio = float (size.x) / float(size.y);
454
455 switch(zoom) {
456
457 case ZOOM_FIT:
458 usableHeight = canvasHeight;
459 if (ratio > 1.0f || mode == ONEPAGE)
460 usableWidth = canvasWidth;
461 else
462 usableWidth = canvasWidth / 2;
463 break;
464
465 case ZOOM_WIDTH:
466 usableHeight = canvasHeight;
467 // For now, to be safe, assume vertical scrollbar is present thanks to neighboring page.
468 if (ratio > 1.0f || mode == ONEPAGE)
469 usableWidth = canvasWidth - scrollbarThickness;
470 else
471 usableWidth = (canvasWidth - scrollbarThickness) / 2;
472 break;
473
474 case ZOOM_HEIGHT:
475 usableWidth = canvasWidth;
476 // For now, to be safe, assume horizontal scrollbar is present thanks to neighboring page.
477 if (ratio > 1.0f || mode == ONEPAGE)
478 usableHeight = canvasHeight - scrollbarThickness;
479 else
480 usableHeight = (canvasHeight - scrollbarThickness) / 2;
481 break;
482
483 default:
484 return true;
485
486 }
487
488 if (size.x > usableWidth || size.y > usableHeight)
489 return true;
490 else
491 return false;
492 }
493
Entry()494 void * ComicBook::Entry()
495 {
496 wxUint32 i, target, high, curr;
497 wxInt32 low;
498 bool scalingHappened;
499 wxInputStream *stream;
500 wxMemoryInputStream *mstream;
501
502 while (!TestDestroy()) {
503 scalingHappened = false;
504 curr = currentPage; // in case this value changes midloop
505
506 // The caching algorithm. First calculates next highest
507 // priority page, then checks to see if that page needs
508 // updating. If no pages need updating, yield the timeslice.
509
510 if (cacheLen >= pageCount) {
511 low = 0;
512 high = pageCount - 1;
513 } else {
514 /* A moving window of size cacheLen. 2/3 of the pages in the
515 * cache are after the Current page, the other 1/3 are before
516 * it. */
517 high = 2 * cacheLen / 3;
518 low = cacheLen - high;
519
520 high = curr + high - 1;
521 low = curr - low;
522
523 /* Keep the window within 0 and pageCount. */
524 if (high >= pageCount) {
525 low -= (high - pageCount) + 1;
526 high = pageCount - 1;
527 } else if (low < 0) {
528 high -= low; // low is negative, this is an addition
529 low = 0;
530 }
531 }
532
533 for (i = 0; i < cacheLen && i < pageCount; i++) {
534 if (TestDestroy())
535 goto thread_end;
536 target = curr + i;
537 if (curr > 0) { // ftw and fth scaling requires previous page to
538 target--; // do FitWithoutScrollbars.
539 if (target > high)
540 target = curr - (++target - high);
541 } else if (target > high)
542 target = curr - (target - high);
543 try {
544 originalLockers[target].Lock();
545 if (originals[target].Ok()) {
546 originalLockers[target].Unlock();
547 continue;
548 }
549 stream = ExtractStream(target);
550 if (stream->IsOk() && stream->GetSize() > 0) {
551 originals[target].LoadFile(*stream);
552 // Memory Input Streams don't take ownership of the buffer
553 mstream = dynamic_cast<wxMemoryInputStream*>(stream);
554 if (mstream)
555 delete[] (wxUint8 *) mstream->GetInputStreamBuffer()->GetBufferStart();
556 } else {
557 SendPageErrorEvent(target, wxString::Format(wxT("Failed to extract page %d."), target));
558 originals[target] = wxImage(1, 1);
559 }
560 if (!originals[target].Ok()) {
561 SendPageErrorEvent(target, wxString::Format(wxT("Failed to extract page %d."), target));
562 originals[target] = wxImage(1, 1);
563 }
564 wxDELETE(stream);
565 } catch (ArchiveException *ae) {
566 SendPageErrorEvent(target, ae->Message);
567 }
568
569 thumbnailLockers[target].Lock();
570 if (!thumbnails[target].Ok())
571 ScaleThumbnail(target);
572 if (!thumbnails[target].Ok()) // let's only see one error if things go wrong
573 SendPageErrorEvent(target, wxString::Format(wxT("Could not create thumbnail for page %d."), target));
574
575 thumbnailLockers[target].Unlock();
576 originalLockers[target].Unlock();
577 break; // extraction happened, we should try a rescale now
578 }
579
580 for (i = 0; i < cacheLen && i < pageCount; i++) {
581 if (TestDestroy())
582 goto thread_end;
583 target = curr + i;
584 if (target > high)
585 target = curr - (target - high);
586
587 resampleLockers[target].Lock();
588
589 if (resamples[target].Ok() || !originals[target].Ok() ||
590 // Only try scaling if the neighbors are extracted. Otherwise,
591 // we can't test whether the neighbors will fit without scrollbars.
592 (target > 0 && !originals[target - 1].Ok()) ||
593 (target < (pageCount - 1) && !originals[target + 1].Ok())) {
594 resampleLockers[target].Unlock();
595 continue;
596 }
597
598 ScaleImage(target);
599 scalingHappened = true;
600 resampleLockers[target].Unlock();
601 break;
602 }
603
604 if (!scalingHappened) {
605 // if the cache is full, then use this iteration to fetch a
606 // thumbnail, if needed.
607 for (wxUint32 j = 0; j < pageCount; j++) {
608 thumbnailLockers[j].Lock();
609
610 if (thumbnails[j].Ok()) {
611 thumbnailLockers[j].Unlock();
612 continue;
613 }
614 try {
615 originalLockers[j].Lock();
616 if (!originals[j].Ok()) {
617 stream = ExtractStream(j);
618 if (stream->IsOk() && stream->GetSize() > 0) {
619 originals[j].LoadFile(*stream);
620 // Memory Input Streams don't take ownership of the buffer
621 mstream = dynamic_cast<wxMemoryInputStream*>(stream);
622 if (mstream)
623 delete[] (wxUint8 *) mstream->GetInputStreamBuffer()->GetBufferStart();
624 } else {
625 SendPageErrorEvent(j, wxString::Format(wxT("Failed to extract page %d."), j));
626 originals[j] = wxImage(1, 1);
627 }
628 if (!originals[j].Ok()) {
629 SendPageErrorEvent(j, wxString::Format(wxT("Failed to extract page %d."), j));
630 originals[j] = wxImage(1, 1);
631 }
632 wxDELETE(stream);
633 }
634 } catch (ArchiveException *ae) {
635 SendPageErrorEvent(j, wxString::Format(wxT("Failed to extract page %d."), j));
636 }
637 ScaleThumbnail(j);
638 originalLockers[j].Unlock();
639 thumbnailLockers[j].Unlock();
640 break;
641 }
642 }
643
644 if (i < cacheLen && i < pageCount) {
645 if (cacheLen < pageCount) {
646 // Delete pages outside of the cache's range.
647 for (i = 0; wxInt32(i) < low; i++) {
648
649 resampleLockers[i].Lock();
650 if(resamples[i].Ok())
651 resamples[i].Destroy();
652 resampleLockers[i].Unlock();
653
654 originalLockers[i].Lock();
655 if(originals[i].Ok())
656 originals[i].Destroy();
657 originalLockers[i].Unlock();
658 }
659
660 for (i = pageCount - 1; i > high; i--) {
661
662 resampleLockers[i].Lock();
663 if(resamples[i].Ok())
664 resamples[i].Destroy();
665 resampleLockers[i].Unlock();
666
667 originalLockers[i].Lock();
668 if(originals[i].Ok())
669 originals[i].Destroy();
670 originalLockers[i].Unlock();
671 }
672 }
673 }
674
675 Yield();
676 Sleep(20);
677 }
678 thread_end:
679 return 0;
680 }
681
682 /* Resizes an image to fit. */
ScaleImage(wxUint32 pagenumber)683 void ComicBook::ScaleImage(wxUint32 pagenumber)
684 {
685 wxInt32 xImage, yImage;
686 float rCanvas, rImage; // width/height ratios
687 float scalingFactor;
688
689 wxImage &orig = originals[pagenumber];
690
691 if (Orientations[pagenumber] == NORTH || Orientations[pagenumber] == SOUTH) {
692 xImage = orig.GetWidth();
693 yImage = orig.GetHeight();
694 } else {// EAST || WEST
695 yImage = orig.GetWidth();
696 xImage = orig.GetHeight();
697 }
698
699 switch(zoom) {
700
701 case ZOOM_CUSTOM:
702 scalingFactor = zoomLevel / 100.0f;
703 break;
704
705 case ZOOM_FIT:
706 if (fitOnlyOversize && !IsOversize(pagenumber))
707 goto zoom_original;
708 rImage = float(xImage) / float(yImage);
709 if (rImage >= 1.0f || mode == ONEPAGE) {
710 rCanvas = float(canvasWidth) / float(canvasHeight);
711 if (rCanvas > rImage)
712 scalingFactor = float(canvasHeight) / float(yImage);
713 else
714 scalingFactor = float(canvasWidth) / float(xImage);
715 } else {
716 rCanvas = (float(canvasWidth)/2.0f) / float(canvasHeight);
717 if (rCanvas > rImage)
718 scalingFactor = float(canvasHeight) / float(yImage);
719 else
720 scalingFactor = (float(canvasWidth)/2.0f) / float(xImage);
721 }
722 break;
723
724 case ZOOM_WIDTH:
725 if (fitOnlyOversize && !IsOversize(pagenumber))
726 goto zoom_original;
727 if (FitWithoutScrollbars(pagenumber, &scalingFactor)) {
728 if (mode == TWOPAGE) {
729 // check neighbor pages to see if they fit too
730 if (pagenumber > 0) {
731 if (!FitWithoutScrollbars(pagenumber - 1))
732 goto fith_nofit;
733 }
734 if (pagenumber < (pageCount - 1)) {
735 if (!FitWithoutScrollbars(pagenumber + 1))
736 goto fith_nofit;
737 }
738 }
739 } else {
740 fith_nofit:
741 rImage = float(xImage) / float(yImage);
742 // fit with scrollbars
743 if (rImage >= 1.0f || mode == ONEPAGE) {
744 scalingFactor = float(canvasWidth - scrollbarThickness) / float(xImage);
745 } else {
746 scalingFactor = float((canvasWidth - scrollbarThickness) / 2) / float(xImage);
747 }
748 }
749 break;
750
751 case ZOOM_HEIGHT: // fit to height
752 if (fitOnlyOversize && !IsOversize(pagenumber))
753 goto zoom_original;
754 if (FitWithoutScrollbars(pagenumber, &scalingFactor)) {
755 if (mode == TWOPAGE) {
756 // check neighbor pages to see if they fit too
757 if (pagenumber > 0) {
758 if (!FitWithoutScrollbars(pagenumber - 1))
759 goto fitv_nofit;
760 }
761 if (pagenumber < pageCount - 1) {
762 if (!FitWithoutScrollbars(pagenumber + 1))
763 goto fitv_nofit;
764 }
765 }
766 } else {
767 fitv_nofit:
768 // fit with scrollbars
769 scalingFactor = float(canvasHeight - scrollbarThickness) / float(yImage);
770 }
771 break;
772
773 case ZOOM_FULL: // no resize
774 default:
775 zoom_original:
776 switch (Orientations[pagenumber]) {
777 case NORTH:
778 resamples[pagenumber] = wxImage(orig);
779 break;
780 case EAST:
781 resamples[pagenumber] = wxImage(orig).Rotate90(true);
782 break;
783 case SOUTH:
784 resamples[pagenumber] = wxImage(orig).Rotate90().Rotate90();
785 break;
786 case WEST:
787 resamples[pagenumber] = wxImage(orig).Rotate90(false);
788 break;
789 default:
790 break;
791 }
792 SendScaledEvent(pagenumber);
793 return;
794 }
795
796 switch (Orientations[pagenumber]) {
797 case NORTH:
798 resamples[pagenumber] = FreeImage_Rescale(orig, wxInt32(xImage * scalingFactor), wxInt32(yImage * scalingFactor), filter);
799 break;
800 case EAST:
801 resamples[pagenumber] = FreeImage_Rescale(orig, wxInt32(yImage * scalingFactor), wxInt32(xImage * scalingFactor), filter).Rotate90(true);
802 break;
803 case SOUTH:
804 resamples[pagenumber] = FreeImage_Rescale(orig, wxInt32(xImage * scalingFactor), wxInt32(yImage * scalingFactor), filter).Rotate90().Rotate90();
805 break;
806 case WEST:
807 resamples[pagenumber] = FreeImage_Rescale(orig, wxInt32(yImage * scalingFactor), wxInt32(xImage * scalingFactor), filter).Rotate90(false);
808 break;
809 }
810 SendScaledEvent(pagenumber);
811 }
812
ScaleThumbnail(wxUint32 pagenumber)813 void ComicBook::ScaleThumbnail(wxUint32 pagenumber)
814 {
815 wxImage &orig = originals[pagenumber];
816 wxInt32 xImage, yImage, xScaled, yScaled;
817 float scalingFactor;
818
819 if (Orientations[pagenumber] == NORTH || Orientations[pagenumber] == SOUTH) {
820 xImage = orig.GetWidth();
821 yImage = orig.GetHeight();
822 } else {// EAST || WEST
823 yImage = orig.GetWidth();
824 xImage = orig.GetHeight();
825 }
826
827 if(float(yImage) / float(xImage) > 0.6f) {
828 yScaled = 60;
829 scalingFactor = 60.0f / float(yImage);
830 xScaled = wxInt32(float(xImage) * scalingFactor);
831 } else {
832 xScaled = 100;
833 scalingFactor = 100.0f / float(xImage);
834 yScaled = wxInt32(float(yImage) * scalingFactor);
835 }
836
837 switch (Orientations[pagenumber]) {
838 case NORTH:
839 thumbnails[pagenumber] = FreeImage_Rescale(orig, xScaled, yScaled, filter);
840 break;
841 case EAST:
842 thumbnails[pagenumber] = FreeImage_Rescale(orig, yScaled, xScaled, filter).Rotate90(true);
843 break;
844 case SOUTH:
845 thumbnails[pagenumber] = FreeImage_Rescale(orig, xScaled, yScaled, filter).Rotate90().Rotate90();
846 break;
847 case WEST:
848 thumbnails[pagenumber] = FreeImage_Rescale(orig, yScaled, xScaled, filter).Rotate90(false);
849 break;
850 }
851 SendThumbnailedEvent(pagenumber);
852 }
853
SetCurrentPage(wxUint32 pagenumber)854 void ComicBook::SetCurrentPage(wxUint32 pagenumber)
855 {
856 currentPage = pagenumber;
857 SendCurrentPageChangedEvent();
858 }
859
SendScaledEvent(wxUint32 pagenumber)860 void ComicBook::SendScaledEvent(wxUint32 pagenumber)
861 {
862 wxCommandEvent event(EVT_PAGE_SCALED, -1);
863 event.SetInt(pagenumber);
864 GetEventHandler()->AddPendingEvent(event);
865 }
866
SendThumbnailedEvent(wxUint32 pagenumber)867 void ComicBook::SendThumbnailedEvent(wxUint32 pagenumber)
868 {
869 wxCommandEvent event(EVT_PAGE_THUMBNAILED, -1);
870 event.SetInt(pagenumber);
871 GetEventHandler()->AddPendingEvent(event);
872 }
873
SendCurrentPageChangedEvent()874 void ComicBook::SendCurrentPageChangedEvent()
875 {
876 wxCommandEvent event(EVT_CURRENT_PAGE_CHANGED, -1);
877 event.SetInt(this->GetCurrentPage());
878 GetEventHandler()->AddPendingEvent(event);
879 }
880
SendPageErrorEvent(wxUint32 pagenumber,wxString message)881 void ComicBook::SendPageErrorEvent(wxUint32 pagenumber, wxString message)
882 {
883 wxCommandEvent event(EVT_PAGE_ERROR, -1);
884 event.SetInt(pagenumber);
885 event.SetString(message);
886 GetEventHandler()->AddPendingEvent(event);
887 }
888
SetPassword(const char * new_password)889 void ComicBook::SetPassword(const char* new_password)
890 {
891 if (!new_password)
892 return;
893 if (password)
894 delete[] password;
895 password = new char[strlen(new_password) + 1];
896 strcpy(password, new_password);
897 }
898
postCtor()899 void ComicBook::postCtor()
900 {
901 Filenames->Sort();
902 Filenames->Shrink();
903
904 pageCount = Filenames->GetCount();
905
906 originals = new wxImage[pageCount];
907 resamples = new wxImage[pageCount];
908 thumbnails = new wxImage[pageCount];
909 originalLockers = new wxMutex[pageCount];
910 resampleLockers = new wxMutex[pageCount];
911 thumbnailLockers = new wxMutex[pageCount];
912 Orientations = new COMICAL_ROTATE[pageCount]; // NORTH == 0
913 for (wxUint32 i = 0; i < pageCount; i++)
914 Orientations[i] = NORTH;
915
916 if (!password) { // the password may already have been set if this is a RAR with encrypted headers
917 wxString new_password;
918 while (!TestPassword()) { // if the password needs to be set
919 new_password = wxGetPasswordFromUser(
920 wxT("This archive is password-protected. Please enter the password."),
921 wxT("Enter Password"));
922 if (new_password.IsEmpty()) // the dialog was cancelled, and the archive cannot be opened
923 throw ArchiveException(filename, wxT("Comical could not open this file because it is password-protected."));
924 SetPassword(new_password.ToAscii());
925 }
926 }
927 }
928