OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2009, Google Inc. | 2 * Copyright 2009, Google Inc. |
3 * All rights reserved. | 3 * All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
7 * met: | 7 * met: |
8 * | 8 * |
9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 19 matching lines...) Expand all Loading... |
30 */ | 30 */ |
31 | 31 |
32 | 32 |
33 // This file contains the image file codec operations for OpenGL texture | 33 // This file contains the image file codec operations for OpenGL texture |
34 // loading. Trying to keep this class as independent from the OpenGL API in | 34 // loading. Trying to keep this class as independent from the OpenGL API in |
35 // case they need retargeting later on. | 35 // case they need retargeting later on. |
36 | 36 |
37 // The precompiled header must appear before anything else. | 37 // The precompiled header must appear before anything else. |
38 #include "core/cross/precompile.h" | 38 #include "core/cross/precompile.h" |
39 | 39 |
| 40 #include "core/cross/bitmap.h" |
40 #include <cstring> | 41 #include <cstring> |
| 42 #include <cmath> |
41 #include <sys/stat.h> | 43 #include <sys/stat.h> |
42 #include "core/cross/bitmap.h" | |
43 #include "utils/cross/file_path_utils.h" | 44 #include "utils/cross/file_path_utils.h" |
44 #include "base/file_path.h" | 45 #include "base/file_path.h" |
45 #include "base/file_util.h" | 46 #include "base/file_util.h" |
46 #include "import/cross/raw_data.h" | 47 #include "import/cross/raw_data.h" |
47 #include "import/cross/memory_buffer.h" | 48 #include "import/cross/memory_buffer.h" |
48 #include "import/cross/memory_stream.h" | 49 #include "import/cross/memory_stream.h" |
49 | 50 |
50 using file_util::OpenFile; | 51 using file_util::OpenFile; |
51 using file_util::CloseFile; | 52 using file_util::CloseFile; |
52 using file_util::GetFileSize; | 53 using file_util::GetFileSize; |
53 | 54 |
| 55 namespace { |
| 56 static const double kEpsilon = 0.0001; |
| 57 } // anonymous namespace. |
| 58 |
54 namespace o3d { | 59 namespace o3d { |
55 | 60 |
| 61 O3D_DEFN_CLASS(Bitmap, ParamObject); |
| 62 |
| 63 Bitmap::Bitmap(ServiceLocator* service_locator) |
| 64 : ParamObject(service_locator), |
| 65 image_data_(NULL), |
| 66 format_(Texture::UNKNOWN_FORMAT), |
| 67 width_(0), |
| 68 height_(0), |
| 69 num_mipmaps_(0), |
| 70 is_cubemap_(false) {} |
| 71 |
56 // Gets the size of the buffer containing a an image, given its width, height | 72 // Gets the size of the buffer containing a an image, given its width, height |
57 // and format. | 73 // and format. |
58 unsigned int Bitmap::GetBufferSize(unsigned int width, | 74 unsigned int Bitmap::GetBufferSize(unsigned int width, |
59 unsigned int height, | 75 unsigned int height, |
60 Texture::Format format) { | 76 Texture::Format format) { |
61 DCHECK(CheckImageDimensions(width, height)); | 77 DCHECK(CheckImageDimensions(width, height)); |
62 unsigned int pixels = width * height; | 78 unsigned int pixels = width * height; |
63 switch (format) { | 79 switch (format) { |
64 case Texture::XRGB8: | 80 case Texture::XRGB8: |
65 case Texture::ARGB8: | 81 case Texture::ARGB8: |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 const uint8* data = raw_data->GetData(); | 270 const uint8* data = raw_data->GetData(); |
255 if (!data) { | 271 if (!data) { |
256 return false; | 272 return false; |
257 } | 273 } |
258 | 274 |
259 MemoryReadStream stream(data, raw_data->GetLength()); | 275 MemoryReadStream stream(data, raw_data->GetLength()); |
260 | 276 |
261 return LoadFromStream(&stream, filename, file_type, generate_mipmaps); | 277 return LoadFromStream(&stream, filename, file_type, generate_mipmaps); |
262 } | 278 } |
263 | 279 |
| 280 void Bitmap::DrawImage(Bitmap* src_img, |
| 281 int src_x, int src_y, |
| 282 int src_width, int src_height, |
| 283 int dst_x, int dst_y, |
| 284 int dst_width, int dst_height) { |
| 285 DCHECK(src_img->image_data()); |
| 286 DCHECK(image_data()); |
| 287 |
| 288 // Clip source and destination rectangles to |
| 289 // source and destination bitmaps. |
| 290 // if src or dest rectangle is out of boundary, |
| 291 // do nothing and return. |
| 292 if (!AdjustDrawImageBoundary(&src_x, &src_y, |
| 293 &src_width, &src_height, |
| 294 src_img->width_, src_img->height_, |
| 295 &dst_x, &dst_y, |
| 296 &dst_width, &dst_height, |
| 297 width_, height_)) |
| 298 return; |
| 299 |
| 300 unsigned int components = 0; |
| 301 // check formats of source and dest images. |
| 302 // format of source and dest should be the same. |
| 303 if (src_img->format_ != format_) { |
| 304 O3D_ERROR(service_locator()) << "DrawImage does not support " |
| 305 << "different formats."; |
| 306 return; |
| 307 } |
| 308 // if src and dest are in the same size and drawImage is copying |
| 309 // the entire bitmap on dest image, just perform memcpy. |
| 310 if (src_x == 0 && src_y == 0 && dst_x == 0 && dst_y == 0 && |
| 311 src_img->width_ == width_ && src_img->height_ == height_ && |
| 312 src_width == src_img->width_ && src_height == src_img->height_ && |
| 313 dst_width == width_ && dst_height == height_) { |
| 314 memcpy(image_data(), src_img->image_data(), GetTotalSize()); |
| 315 return; |
| 316 } |
| 317 |
| 318 // if drawImage is not copying the whole bitmap, we need to check |
| 319 // the format. currently only support XRGB8 and ARGB8 |
| 320 if (src_img->format_ == Texture::XRGB8 || |
| 321 src_img->format_ == Texture::ARGB8) { |
| 322 components = 4; |
| 323 } else { |
| 324 O3D_ERROR(service_locator()) << "DrawImage does not support format: " |
| 325 << src_img->format_ << " unless src and " |
| 326 << "dest images are in the same size and " |
| 327 << "copying the entire bitmap"; |
| 328 return; |
| 329 } |
| 330 |
| 331 unsigned char* src_img_data = src_img->image_data(); |
| 332 unsigned char* dst_img_data = image_data(); |
| 333 |
| 334 // crop part of image from src img, scale it in |
| 335 // bilinear interpolation fashion, and paste it |
| 336 // on dst img. |
| 337 BilinearInterpolateScale(src_img_data, src_x, src_y, |
| 338 src_width, src_height, |
| 339 src_img->width_, src_img->height_, |
| 340 dst_img_data, dst_x, dst_y, |
| 341 dst_width, dst_height, |
| 342 width_, height_, components); |
| 343 } |
| 344 |
| 345 // static utility function used by DrawImage in bitmap and textures. |
| 346 // in this function, positions are converted to 4th-quadrant, which |
| 347 // means origin locates left-up corner. |
| 348 void Bitmap::BilinearInterpolateScale(const uint8* src_img_data, |
| 349 int src_x, int src_y, |
| 350 int src_width, int src_height, |
| 351 int src_img_width, int src_img_height, |
| 352 uint8* dest_img_data, |
| 353 int dest_x, int dest_y, |
| 354 int dest_width, int dest_height, |
| 355 int dest_img_width, int dest_img_height, |
| 356 int components) { |
| 357 for (int i = 0; i < std::abs(dest_width); i++) { |
| 358 // x is the iterator of dest_width in dest_img. |
| 359 // change x to negative if dest_width is negative. |
| 360 int x = i; |
| 361 if (dest_width < 0) |
| 362 x = -i; |
| 363 |
| 364 // calculate corresponding coordinate in src_img. |
| 365 double base_x = i * (std::abs(src_width) - 1) / |
| 366 static_cast<double>(std::abs(dest_width) - 1); |
| 367 // base_floor_x is the iterator of src_width in src_img. |
| 368 // change base_x to negative if src_width is negative. |
| 369 if (src_width < 0) |
| 370 base_x = -base_x; |
| 371 int base_floor_x = static_cast<int>(std::floor(base_x)); |
| 372 |
| 373 for (int j = 0; j < std::abs(dest_height); j++) { |
| 374 // y is the iterator of dest_height in dest_img. |
| 375 // change y to negative if dest_height is negative. |
| 376 int y = j; |
| 377 if (dest_height < 0) |
| 378 y = -j; |
| 379 |
| 380 // calculate coresponding coordinate in src_img. |
| 381 double base_y = j * (std::abs(src_height) - 1) / |
| 382 static_cast<double>(std::abs(dest_height) - 1); |
| 383 // change base_y to negative if src_height is negative. |
| 384 if (src_height < 0) |
| 385 base_y = -base_y; |
| 386 int base_floor_y = static_cast<int>(std::floor(base_y)); |
| 387 |
| 388 for (unsigned int c = 0; c < components; c++) { |
| 389 // if base_x and base_y are integers, which means this point |
| 390 // exists in src_img, just copy the original values. |
| 391 if (base_x - base_floor_x < kEpsilon && |
| 392 base_y - base_floor_y < kEpsilon) { |
| 393 dest_img_data[((dest_img_height - (y + dest_y) - 1) * |
| 394 dest_img_width + dest_x + x) * components + c] = |
| 395 src_img_data[((src_img_height - (base_floor_y + src_y) - 1) * |
| 396 src_img_width + src_x + base_floor_x) * components + c]; |
| 397 continue; |
| 398 } |
| 399 |
| 400 // get four nearest neighbors of point (base_x, base_y) from src img. |
| 401 uint8 src_neighbor_11, src_neighbor_21, |
| 402 src_neighbor_12, src_neighbor_22; |
| 403 src_neighbor_11 = src_img_data[((src_img_height - (base_floor_y + |
| 404 src_y) - 1) * src_img_width + src_x + |
| 405 base_floor_x) * components + c]; |
| 406 // if base_x exists in src img. set src_neighbor_21 to src_neighbor_11 |
| 407 // so the interpolation result would remain src_neighbor_11. |
| 408 if (base_x - base_floor_x < kEpsilon) |
| 409 src_neighbor_21 = src_neighbor_11; |
| 410 else |
| 411 src_neighbor_21 = src_img_data[((src_img_height - (base_floor_y + |
| 412 src_y) - 1) * src_img_width + src_x + |
| 413 base_floor_x + 1) * components + c]; |
| 414 // if base_y exists in src img. set src_neighbor_12 to src_neighbor_11 |
| 415 // so the interpolation result would remain src_neighbor_11. |
| 416 if (base_y - base_floor_y < kEpsilon) |
| 417 src_neighbor_12 = src_neighbor_11; |
| 418 else |
| 419 src_neighbor_12 = src_img_data[((src_img_height - (base_floor_y + |
| 420 src_y) - 2) * src_img_width + src_x + |
| 421 base_floor_x) * components + c]; |
| 422 |
| 423 if (base_x - base_floor_x < kEpsilon) |
| 424 src_neighbor_22 = src_neighbor_21; |
| 425 else if (base_y - base_floor_y < kEpsilon) |
| 426 src_neighbor_22 = src_neighbor_12; |
| 427 else |
| 428 src_neighbor_22 = src_img_data[((src_img_height - (base_floor_y + |
| 429 src_y) - 2) * src_img_width + src_x + |
| 430 base_floor_x + 1) * components + c]; |
| 431 |
| 432 // calculate interpolated value. |
| 433 double interpolatedValue = (1 - (base_y - base_floor_y)) * |
| 434 ((base_x - base_floor_x) * |
| 435 src_neighbor_21 + |
| 436 (1 - (base_x - base_floor_x)) * |
| 437 src_neighbor_11) + |
| 438 (base_y - base_floor_y) * |
| 439 ((base_x - base_floor_x) * |
| 440 src_neighbor_22 + |
| 441 (1 - (base_x - base_floor_x)) * |
| 442 src_neighbor_12); |
| 443 |
| 444 // assign the nearest integer of interpolatedValue to dest_img_data. |
| 445 dest_img_data[((dest_img_height - (y + dest_y) - 1) * dest_img_width + |
| 446 dest_x + x) * components + c] = |
| 447 static_cast<uint8>(interpolatedValue + 0.5); |
| 448 } |
| 449 } |
| 450 } |
| 451 } |
264 | 452 |
265 Bitmap::ImageFileType Bitmap::GetFileTypeFromFilename(const char *filename) { | 453 Bitmap::ImageFileType Bitmap::GetFileTypeFromFilename(const char *filename) { |
266 // Convert the filename to lower case for matching. | 454 // Convert the filename to lower case for matching. |
267 // NOTE: Surprisingly, "tolower" is not in the std namespace. | 455 // NOTE: Surprisingly, "tolower" is not in the std namespace. |
268 String name(filename); | 456 String name(filename); |
269 std::transform(name.begin(), name.end(), name.begin(), ::tolower); | 457 std::transform(name.begin(), name.end(), name.begin(), ::tolower); |
270 | 458 |
271 // Dispatch loading functions based on filename extensions. | 459 // Dispatch loading functions based on filename extensions. |
272 String::size_type i = name.rfind("."); | 460 String::size_type i = name.rfind("."); |
273 if (i == String::npos) { | 461 if (i == String::npos) { |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
567 DCHECK_LT(base_x, src_width); | 755 DCHECK_LT(base_x, src_width); |
568 for (unsigned int c = 0; c < components; ++c) { | 756 for (unsigned int c = 0; c < components; ++c) { |
569 dst[(y * dst_width + x) * components + c] = | 757 dst[(y * dst_width + x) * components + c] = |
570 src[(base_y * src_width + base_x) * components + c]; | 758 src[(base_y * src_width + base_x) * components + c]; |
571 } | 759 } |
572 } | 760 } |
573 } | 761 } |
574 return true; | 762 return true; |
575 } | 763 } |
576 | 764 |
| 765 // Adjust boundaries when using DrawImage function in bitmap or texture. |
| 766 bool Bitmap::AdjustDrawImageBoundary(int* src_x, int* src_y, |
| 767 int* src_width, int* src_height, |
| 768 int src_bmp_width, int src_bmp_height, |
| 769 int* dest_x, int* dest_y, |
| 770 int* dest_width, int* dest_height, |
| 771 int dest_bmp_width, int dest_bmp_height) { |
| 772 // if src or dest rectangle is out of boundaries, do nothing. |
| 773 if ((*src_x < 0 && *src_x + *src_width <= 0) || |
| 774 (*src_y < 0 && *src_y + *src_height <= 0) || |
| 775 (*dest_x < 0 && *dest_x + *dest_width <= 0) || |
| 776 (*dest_y < 0 && *dest_y + *dest_height <= 0) || |
| 777 (*src_x >= src_bmp_width && |
| 778 *src_x + *src_width >= src_bmp_width - 1) || |
| 779 (*src_y >= src_bmp_height && |
| 780 *src_y + *src_height >= src_bmp_height - 1) || |
| 781 (*dest_x >= dest_bmp_width && |
| 782 *dest_x + *dest_width >= dest_bmp_width - 1) || |
| 783 (*dest_y >= dest_bmp_height && |
| 784 *dest_y + *dest_height >= dest_bmp_height - 1)) |
| 785 return false; |
| 786 |
| 787 // if start points are negative. |
| 788 // check whether src_x is negative. |
| 789 if (!AdjustDrawImageBoundHelper(src_x, dest_x, |
| 790 src_width, dest_width, src_bmp_width)) |
| 791 return false; |
| 792 // check whether dest_x is negative. |
| 793 if (!AdjustDrawImageBoundHelper(dest_x, src_x, |
| 794 dest_width, src_width, dest_bmp_width)) |
| 795 return false; |
| 796 // check whether src_y is negative. |
| 797 if (!AdjustDrawImageBoundHelper(src_y, dest_y, |
| 798 src_height, dest_height, src_bmp_height)) |
| 799 return false; |
| 800 // check whether dest_y is negative. |
| 801 if (!AdjustDrawImageBoundHelper(dest_y, src_y, |
| 802 dest_height, src_height, dest_bmp_height)) |
| 803 return false; |
| 804 |
| 805 // check any width or height becomes negative after adjustment. |
| 806 if (*src_width == 0 || *src_height == 0 || |
| 807 *dest_width == 0 || *dest_height == 0) { |
| 808 return false; |
| 809 } |
| 810 |
| 811 return true; |
| 812 } |
| 813 |
| 814 // utility function called in AdjustDrawImageBoundary. |
| 815 // help to adjust a specific dimension, |
| 816 // if start point or ending point is out of boundary. |
| 817 bool Bitmap::AdjustDrawImageBoundHelper(int* src_a, int* dest_a, |
| 818 int* src_length, int* dest_length, |
| 819 int src_bmp_length) { |
| 820 if (*src_length == 0 || *dest_length == 0) |
| 821 return false; |
| 822 |
| 823 // check if start point is out of boundary. |
| 824 // if src_a < 0, src_length must be positive. |
| 825 if (*src_a < 0) { |
| 826 int src_length_delta = 0 - *src_a; |
| 827 *dest_a = *dest_a + (*dest_length) * src_length_delta / (*src_length); |
| 828 *dest_length = *dest_length - (*dest_length) * |
| 829 src_length_delta / (*src_length); |
| 830 *src_length = *src_length - src_length_delta; |
| 831 *src_a = 0; |
| 832 } |
| 833 // if src_a >= src_bmp_width, src_length must be negative. |
| 834 if (*src_a >= src_bmp_length) { |
| 835 int src_length_delta = *src_a - (src_bmp_length - 1); |
| 836 *dest_a = *dest_a - (*dest_length) * src_length_delta / (*src_length); |
| 837 *dest_length = *dest_length - (*dest_length) * |
| 838 src_length_delta / *src_length; |
| 839 *src_length = *src_length - src_length_delta; |
| 840 *src_a = src_bmp_length - 1; |
| 841 } |
| 842 |
| 843 if (*src_length == 0 || *dest_length == 0) |
| 844 return false; |
| 845 // check whether start point + related length is out of boundary. |
| 846 // if src_a + src_length > src_bmp_length, src_length must be positive. |
| 847 if (*src_a + *src_length > src_bmp_length) { |
| 848 int src_length_delta = *src_length - (src_bmp_length - *src_a); |
| 849 *dest_length = *dest_length - (*dest_length) * |
| 850 src_length_delta / (*src_length); |
| 851 *src_length = *src_length - src_length_delta; |
| 852 } |
| 853 // if src_a + src_length < -1, src_length must be negative. |
| 854 if (*src_a + *src_length < -1) { |
| 855 int src_length_delta = 0 - (*src_a + *src_length); |
| 856 *dest_length = *dest_length + (*dest_length) * |
| 857 src_length_delta / (*src_length); |
| 858 *src_length = *src_length + src_length_delta; |
| 859 } |
| 860 |
| 861 return true; |
| 862 } |
| 863 |
577 // Checks that all the alpha values are 1.0 | 864 // Checks that all the alpha values are 1.0 |
578 bool Bitmap::CheckAlphaIsOne() const { | 865 bool Bitmap::CheckAlphaIsOne() const { |
579 if (!image_data()) | 866 if (!image_data()) |
580 return false; | 867 return false; |
581 | 868 |
582 switch (format()) { | 869 switch (format()) { |
583 case Texture::XRGB8: | 870 case Texture::XRGB8: |
584 return true; | 871 return true; |
585 case Texture::ARGB8: { | 872 case Texture::ARGB8: { |
586 int faces = is_cubemap() ? 6 : 1; | 873 int faces = is_cubemap() ? 6 : 1; |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
675 } | 962 } |
676 } | 963 } |
677 break; | 964 break; |
678 } | 965 } |
679 case Texture::UNKNOWN_FORMAT: | 966 case Texture::UNKNOWN_FORMAT: |
680 return false; | 967 return false; |
681 } | 968 } |
682 return true; | 969 return true; |
683 } | 970 } |
684 | 971 |
| 972 ObjectBase::Ref Bitmap::Create(ServiceLocator* service_locator) { |
| 973 return ObjectBase::Ref(new Bitmap(service_locator)); |
| 974 } |
| 975 |
685 } // namespace o3d | 976 } // namespace o3d |
OLD | NEW |