00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <string.h>
00028 #ifdef WIN32
00029 #include <windows.h>
00030 #else
00031 #include <sys/sysinfo.h>
00032 #endif
00033 #include <time.h>
00034
00035 #include <cassert>
00036 #include <gdk-pixbuf/gdk-pixbuf-transform.h>
00037
00038 #include "plugin_config.h"
00039
00040 #include "image_page.h"
00041 #include "image_dither.h"
00042 #include "utils.h"
00043 #include "log.h"
00044
00045 namespace images
00046 {
00047
00048 bool ImagePage::g_type_initialize;
00049
00050 ImagePage::ImagePage(const std::string & p,
00051 const ImagePageAttrs & request)
00052 : path(p)
00053 , in_use(false)
00054 , timestamp(0)
00055 , loader(0)
00056 , valid(false)
00057 {
00058 LOGPRINTF("%s", p.c_str());
00059
00060 attrs.original_width = request.original_width;
00061 attrs.original_height = request.original_height;
00062 attrs.desired_width = request.desired_width;
00063 attrs.desired_height = request.desired_height;
00064 attrs.desired_color_depth = request.desired_color_depth;
00065
00066 attrs.zoom = request.zoom;
00067 attrs.rotation = request.rotation;
00068 attrs.final_rotation = request.final_rotation;
00069
00070 display_width = request.desired_width;
00071 display_height = request.desired_height;
00072 }
00073
00074 ImagePage::~ImagePage(void)
00075 {
00076 LOGPRINTF("entry %s", path.c_str());
00077
00078 destroy();
00079 }
00080
00081 void ImagePage::init_type_system()
00082 {
00083 LOGPRINTF("entry");
00084
00085 if (!g_type_initialize)
00086 {
00087 g_type_initialize = true;
00088
00089
00090
00091
00092 }
00093 }
00094
00095
00096
00097
00098 bool ImagePage::render(void)
00099 {
00100 LOGPRINTF("entry: anchor [%s]", anchor.c_str());
00101
00102 if (!try_to_load_at_scale())
00103 {
00104 return false;
00105 }
00106
00107 if (!update_attributes())
00108 {
00109 return false;
00110 }
00111
00112 if (!try_to_dither())
00113 {
00114 return false;
00115 }
00116
00117 if (!try_to_rotate_grey())
00118 {
00119 return false;
00120 }
00121
00122 return true;
00123 }
00124
00125 void ImagePage::calc_desired_dimension(ImagePageAttrs & request)
00126 {
00127 int w = request.original_width;
00128 int h = request.original_height;
00129 unsigned int display_w = request.desired_width;
00130 unsigned int display_h = request.desired_height;
00131
00132 PluginRotationDegree autorotate = check_autorotate(w, h, display_w, display_h);
00133
00134
00135 int rotation = static_cast<int>(request.rotation);
00136 rotation = (rotation + autorotate) % Clockwise_Degrees_360;
00137 request.final_rotation = static_cast<PluginRotationDegree>(rotation);
00138 if ( rotation == Clockwise_Degrees_90
00139 || rotation == Clockwise_Degrees_270)
00140 {
00141 w = request.original_height;
00142 h = request.original_width;
00143 request.original_width = w;
00144 request.original_height = h;
00145 }
00146
00147
00148 if (request.zoom == PLUGIN_ZOOM_TO_PAGE ||
00149 request.zoom == PLUGIN_ZOOM_TO_CROP_BY_PAGE)
00150 {
00151 float ratio_width = static_cast<float>(request.desired_width) /
00152 static_cast<float>(w);
00153
00154 float ratio_height = static_cast<float>(request.desired_height) /
00155 static_cast<float>(h);
00156
00157 if (ratio_width > ratio_height)
00158 {
00159 request.desired_width = static_cast<int>(ratio_height * w);
00160 }
00161 else
00162 {
00163 request.desired_height = static_cast<int>(ratio_width * h);
00164 }
00165 }
00166 else if (request.zoom == PLUGIN_ZOOM_TO_WIDTH ||
00167 request.zoom == PLUGIN_ZOOM_TO_CROP_BY_WIDTH)
00168 {
00169 float ratio_width = static_cast<float>(request.desired_width) /
00170 static_cast<float>(w);
00171 request.desired_height = static_cast<int>(h * ratio_width);
00172 }
00173 else if (request.zoom > 0)
00174 {
00175 request.desired_width = static_cast<int>(
00176 static_cast<float>(request.zoom * w) / static_cast<float>(100));
00177 request.desired_height = static_cast<int>(
00178 static_cast<float>(request.zoom * h) / static_cast<float>(100));
00179 }
00180 }
00181
00182 bool ImagePage::try_to_load_at_scale(void)
00183 {
00184
00185 if (loader)
00186 {
00187 return true;
00188 }
00189
00190
00191 calc_desired_dimension(attrs);
00192
00193
00194 gint w, h;
00195 if (attrs.final_rotation == Clockwise_Degrees_90 ||
00196 attrs.final_rotation == Clockwise_Degrees_270 )
00197 {
00198 w = attrs.desired_height;
00199 h = attrs.desired_width;
00200 }
00201 else
00202 {
00203 w = attrs.desired_width;
00204 h = attrs.desired_height;
00205 }
00206
00207 GError *err = 0;
00208 loader = gdk_pixbuf_new_from_file_at_size(path.c_str(), w, h, &err);
00209 if (loader == 0)
00210 {
00211 ERRORPRINTF("load error at size [%d x %d] error [%s]", w, h, err ? err->message : "?");
00212 if (err) { g_error_free(err); err = 0; }
00213 return false;
00214 }
00215
00216
00217 attrs.original_color_depth = gdk_pixbuf_get_bits_per_sample(loader)
00218 * gdk_pixbuf_get_n_channels(loader);
00219 return true;
00220 }
00221
00222 bool ImagePage::try_to_rotate_grey(void)
00223 {
00224 LOGPRINTF("entry");
00225 g_assert(attrs.data != 0);
00226 g_assert(attrs.desired_color_depth == 8);
00227
00228 ImagePageAttrs new_attrs(attrs);
00229 new_attrs.data = 0;
00230 guchar *src, *dst;
00231 gint x, y;
00232
00233
00234 switch (attrs.final_rotation)
00235 {
00236 case Clockwise_Degrees_0:
00237 case Clockwise_Degrees_360:
00238
00239 LOGPRINTF("No need to rotate.");
00240 break;
00241
00242 case Clockwise_Degrees_90:
00243
00244 new_attrs.desired_width = attrs.desired_height;
00245 new_attrs.desired_height = attrs.desired_width;
00246 new_attrs.row_stride = ImageDither::get_rowstride(new_attrs.desired_width, 1, 4);
00247
00248
00249 new_attrs.data = new unsigned char[ new_attrs.row_stride * new_attrs.desired_height ];
00250 if (new_attrs.data == 0)
00251 {
00252 ERRORPRINTF("Failed to allocate new bitmap.");
00253 return false;
00254 }
00255
00256
00257 for (y = 0; y < attrs.desired_height; y++)
00258 {
00259 src = attrs.data
00260 + (y * attrs.row_stride);
00261 dst = new_attrs.data
00262 + (new_attrs.desired_width - y - 1);
00263 for (x = 0; x < attrs.desired_width; x++)
00264 {
00265 *dst = *src;
00266 src = src + 1;
00267 dst = dst + new_attrs.row_stride;
00268 }
00269 }
00270 break;
00271
00272 case Clockwise_Degrees_180:
00273
00274 new_attrs.data = new unsigned char[ new_attrs.row_stride * new_attrs.desired_height ];
00275 if (new_attrs.data == 0)
00276 {
00277 ERRORPRINTF("Failed to allocate new bitmap.");
00278 return false;
00279 }
00280
00281
00282 for (y = 0; y < attrs.desired_height; y++)
00283 {
00284 src = attrs.data
00285 + (y * attrs.row_stride);
00286 dst = new_attrs.data
00287 + new_attrs.desired_width - 1
00288 + ((attrs.desired_height - y - 1) * new_attrs.row_stride);
00289 for (x = 0; x < attrs.desired_width; x++)
00290 {
00291 *dst = *src;
00292 src = src + 1;
00293 dst = dst - 1;
00294 }
00295 }
00296 break;
00297
00298 case Clockwise_Degrees_270:
00299
00300 new_attrs.desired_width = attrs.desired_height;
00301 new_attrs.desired_height = attrs.desired_width;
00302 new_attrs.row_stride = ImageDither::get_rowstride(new_attrs.desired_width, 1, 4);
00303
00304
00305 new_attrs.data = new unsigned char[ new_attrs.row_stride * new_attrs.desired_height ];
00306 if (new_attrs.data == 0)
00307 {
00308 ERRORPRINTF("Failed to allocate new bitmap.");
00309 return false;
00310 }
00311
00312
00313 for (y = 0; y < attrs.desired_height; y++)
00314 {
00315 src = attrs.data
00316 + (y * attrs.row_stride);
00317 dst = new_attrs.data
00318 + y
00319 + ((new_attrs.desired_height - 1) * new_attrs.row_stride);
00320 for (x = 0; x < attrs.desired_width; x++)
00321 {
00322 *dst = *src;
00323 src = src + 1;
00324 dst = dst - new_attrs.row_stride;
00325 }
00326 }
00327 break;
00328
00329 default:
00330 ERRORPRINTF("invalid attrs.rotation [%d]", attrs.final_rotation);
00331 assert(false);
00332 }
00333
00334
00335 if (new_attrs.data)
00336 {
00337 delete[] attrs.data;
00338 attrs = new_attrs;
00339 }
00340
00341 return true;
00342 }
00343
00344 bool ImagePage::try_to_dither(void)
00345 {
00346 LOGPRINTF("entry");
00347 g_assert(loader != 0);
00348 g_assert(attrs.data == 0);
00349
00350 if (attrs.original_color_depth == attrs.desired_color_depth)
00351 {
00352 return true;
00353 }
00354
00355
00356 assert(attrs.desired_color_depth == 8);
00357
00358 using namespace images;
00359 BitmapAttributes attrs_src;
00360 BitmapAttributes attrs_dst;
00361
00362 attrs_src.width = attrs.desired_width;
00363 attrs_src.height = attrs.desired_height;
00364 attrs_src.rowstride = attrs.row_stride;
00365 attrs_src.data = gdk_pixbuf_get_pixels(loader);
00366 attrs_src.bytes_per_pixel = attrs.original_color_depth / 8;
00367
00368 ImageDither dither;
00369 dither.dither_to_8bits(&attrs_src, &attrs_dst);
00370
00371
00372 g_object_unref(loader);
00373 loader = 0;
00374
00375
00376 attrs.data = attrs_dst.data;
00377 attrs.row_stride = attrs_dst.rowstride;
00378
00379 return true;
00380 }
00381
00382 bool ImagePage::update_attributes(void)
00383 {
00384 LOGPRINTF("entry");
00385
00386
00387 if (loader)
00388 {
00389 attrs.row_stride = gdk_pixbuf_get_rowstride(loader);
00390 attrs.desired_width = gdk_pixbuf_get_width(loader);
00391 attrs.desired_height = gdk_pixbuf_get_height(loader);
00392 attrs.data = 0;
00393
00394 LOGPRINTF("\noriginal_width = %d,"
00395 "\noriginal_height = %d,"
00396 "\ndesired_width = %d,"
00397 "\ndesired_height = %d,"
00398 "\nrow_stride = %d,"
00399 "\noriginal_color_depth = %d,"
00400 "\ndesired_color_depth = %d,"
00401 "\nzoom = %f,"
00402 "\nrotation = %d,"
00403 "\nfinal_rotation = %d,"
00404 "\ndata = %p",
00405 attrs.original_width,
00406 attrs.original_height,
00407 attrs.desired_width,
00408 attrs.desired_height,
00409 attrs.row_stride,
00410 attrs.original_color_depth,
00411 attrs.desired_color_depth,
00412 attrs.zoom,
00413 attrs.rotation,
00414 attrs.final_rotation,
00415 attrs.data);
00416
00417 return valid = true;
00418 }
00419
00420
00421 return valid = false;
00422 }
00423
00424 void ImagePage::destroy()
00425 {
00426 LOGPRINTF("entry");
00427
00428 path.clear();
00429
00430 attrs.original_width = 0;
00431 attrs.original_height = 0;
00432 attrs.desired_width = 0;
00433 attrs.desired_height = 0;
00434 attrs.row_stride = 0;
00435 attrs.original_color_depth = 0;
00436 attrs.desired_color_depth = DEFAULT_COLOR_DEPTH;
00437 attrs.zoom = DEFAULT_ZOOM_FACTOR;
00438 attrs.rotation = DEFAULT_ROTATION;
00439 attrs.final_rotation = DEFAULT_ROTATION;
00440 if (attrs.data)
00441 {
00442 delete [] attrs.data;
00443 attrs.data = 0;
00444 }
00445
00446 display_width = 0;
00447 display_height = 0;
00448
00449 timestamp = 0;
00450 valid = false;
00451 }
00452
00453
00454 size_t ImagePage::operator()(void) const
00455 {
00456 return calc_key(path, display_width, display_height,
00457 attrs.zoom, attrs.rotation);
00458 }
00459
00460 size_t ImagePage::calc_key(const std::string & anchor,
00461 int width,
00462 int height,
00463 float zoom,
00464 int rotation)
00465 {
00466 struct PageKey
00467 {
00468 char anchor[MAX_PATH_LEN];
00469 int display_width;
00470 int display_height;
00471 float zoom;
00472 int rotation;
00473 };
00474
00475 PageKey key;
00476 int len = sizeof(PageKey);
00477 memset(&key, 0, len);
00478
00479 memcpy(key.anchor, anchor.c_str(), strlen(anchor.c_str()));
00480 key.display_width = width;
00481 key.display_height = height;
00482 key.zoom = zoom;
00483 key.rotation = rotation;
00484
00485 size_t h = make_hash_code((const char*)&key, len);
00486
00487 LOGPRINTF("return %d", h);
00488
00489 return h;
00490 }
00491
00492
00493 bool ImagePage::operator == (const ImagePage & right)
00494 {
00495 LOGPRINTF("entry");
00496
00497
00498 if ((path == right.path)
00499 && (this->attrs.zoom == right.attrs.zoom)
00500 && (this->attrs.rotation == right.attrs.rotation)
00501 && (this->display_width == right.display_width)
00502 && (this->display_height == right.display_height))
00503 {
00504 return true;
00505 }
00506
00507 return false;
00508 }
00509
00510
00511 bool ImagePage::operator < (const ImagePage & right)
00512 {
00513 if (this->in_use && !right.in_use)
00514 {
00515 return false;
00516 }
00517 else if (!this->in_use && right.in_use)
00518 {
00519 return true;
00520 }
00521 else
00522 {
00523 LOGPRINTF("%ld < %ld ? %d",
00524 this->timestamp, right.timestamp,
00525 this->timestamp < right.timestamp);
00526
00527 return (this->timestamp < right.timestamp);
00528 }
00529 }
00530
00531
00532 bool ImagePage::operator > (const ImagePage & right)
00533 {
00534 return !((*this) < right);
00535 }
00536
00537
00538 int ImagePage::length(void)
00539 {
00540 int size = attrs.row_stride * attrs.desired_height;
00541
00542 LOGPRINTF("return %d", size);
00543
00544 return size;
00545 }
00546
00547 gint64 ImagePage::estimate_length(const ImagePageAttrs & request)
00548
00549 {
00550 ImagePageAttrs attrs(request);
00551
00552 calc_desired_dimension(attrs);
00553
00554
00555 int rowstride = ImageDither::get_rowstride(attrs.desired_width,
00556 attrs.desired_color_depth / 8,
00557 4);
00558 gint64 size_for_bitmap = static_cast<gint64>(rowstride) *
00559 static_cast<gint64>(attrs.desired_height);
00560
00561
00562 gint64 size_for_tmp = size_for_bitmap * 5;
00563
00564
00565 gint64 size = size_for_bitmap + size_for_tmp;
00566
00567 LOGPRINTF("original [%d %d] desired [%d %d] memory [%lld %lld %lld]",
00568 attrs.original_width, attrs.original_height,
00569 attrs.desired_width, attrs.desired_height,
00570 size_for_bitmap, size_for_tmp, size);
00571
00572 return size;
00573 }
00574
00575 void ImagePage::update_timestamp(void)
00576 {
00577 #ifdef WIN32
00578 timestamp = GetTickCount();
00579 #else
00580 struct sysinfo s_info;
00581 sysinfo(&s_info);
00582 timestamp = s_info.uptime;
00583 #endif
00584 }
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604 PluginRotationDegree ImagePage::check_autorotate(int w, int h, int display_w, int display_h)
00605 {
00606
00607 if ((w <= display_w) && (h <= display_h))
00608 {
00609 return Clockwise_Degrees_0;
00610 }
00611
00612
00613 if ((w < h) && (display_w > display_h))
00614 {
00615 return Clockwise_Degrees_270;
00616 }
00617
00618
00619 if ((w > h) && (display_w < display_h))
00620 {
00621 return Clockwise_Degrees_90;
00622 }
00623
00624 return Clockwise_Degrees_0;
00625 }
00626
00627 };
00628
00629