From 128ff759facb55e3c0c1326e97c00b325ac3f484 Mon Sep 17 00:00:00 2001 From: "yux@google.com" Date: Wed, 15 Jul 2009 01:07:36 +0000 Subject: expose bitmap in js. Review URL: http://codereview.chromium.org/150058 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20700 0039d316-1c4b-4281-b951-d872f2087c98 --- o3d/core/cross/bitmap.cc | 293 +++++++++++++- o3d/core/cross/bitmap.h | 781 +++++++++++++++++++++---------------- o3d/core/cross/bitmap_test.cc | 739 +++++++++++++++++++++++++++-------- o3d/core/cross/class_manager.cc | 1 + o3d/core/cross/pack.cc | 52 ++- o3d/core/cross/pack.h | 26 +- o3d/core/cross/renderer_test.cc | 16 +- o3d/core/cross/texture.cc | 154 ++++++++ o3d/core/cross/texture.h | 38 ++ o3d/core/win/d3d9/renderer_d3d9.cc | 26 +- o3d/core/win/d3d9/texture_d3d9.cc | 64 +-- o3d/core/win/d3d9/texture_d3d9.h | 4 +- 12 files changed, 1626 insertions(+), 568 deletions(-) (limited to 'o3d/core') diff --git a/o3d/core/cross/bitmap.cc b/o3d/core/cross/bitmap.cc index 947d68ea..d3d1499 100644 --- a/o3d/core/cross/bitmap.cc +++ b/o3d/core/cross/bitmap.cc @@ -37,9 +37,10 @@ // The precompiled header must appear before anything else. #include "core/cross/precompile.h" +#include "core/cross/bitmap.h" #include +#include #include -#include "core/cross/bitmap.h" #include "utils/cross/file_path_utils.h" #include "base/file_path.h" #include "base/file_util.h" @@ -51,8 +52,23 @@ using file_util::OpenFile; using file_util::CloseFile; using file_util::GetFileSize; +namespace { +static const double kEpsilon = 0.0001; +} // anonymous namespace. + namespace o3d { + O3D_DEFN_CLASS(Bitmap, ParamObject); + +Bitmap::Bitmap(ServiceLocator* service_locator) + : ParamObject(service_locator), + image_data_(NULL), + format_(Texture::UNKNOWN_FORMAT), + width_(0), + height_(0), + num_mipmaps_(0), + is_cubemap_(false) {} + // Gets the size of the buffer containing a an image, given its width, height // and format. unsigned int Bitmap::GetBufferSize(unsigned int width, @@ -261,6 +277,178 @@ bool Bitmap::LoadFromRawData(RawData *raw_data, return LoadFromStream(&stream, filename, file_type, generate_mipmaps); } +void Bitmap::DrawImage(Bitmap* src_img, + int src_x, int src_y, + int src_width, int src_height, + int dst_x, int dst_y, + int dst_width, int dst_height) { + DCHECK(src_img->image_data()); + DCHECK(image_data()); + + // Clip source and destination rectangles to + // source and destination bitmaps. + // if src or dest rectangle is out of boundary, + // do nothing and return. + if (!AdjustDrawImageBoundary(&src_x, &src_y, + &src_width, &src_height, + src_img->width_, src_img->height_, + &dst_x, &dst_y, + &dst_width, &dst_height, + width_, height_)) + return; + + unsigned int components = 0; + // check formats of source and dest images. + // format of source and dest should be the same. + if (src_img->format_ != format_) { + O3D_ERROR(service_locator()) << "DrawImage does not support " + << "different formats."; + return; + } + // if src and dest are in the same size and drawImage is copying + // the entire bitmap on dest image, just perform memcpy. + if (src_x == 0 && src_y == 0 && dst_x == 0 && dst_y == 0 && + src_img->width_ == width_ && src_img->height_ == height_ && + src_width == src_img->width_ && src_height == src_img->height_ && + dst_width == width_ && dst_height == height_) { + memcpy(image_data(), src_img->image_data(), GetTotalSize()); + return; + } + + // if drawImage is not copying the whole bitmap, we need to check + // the format. currently only support XRGB8 and ARGB8 + if (src_img->format_ == Texture::XRGB8 || + src_img->format_ == Texture::ARGB8) { + components = 4; + } else { + O3D_ERROR(service_locator()) << "DrawImage does not support format: " + << src_img->format_ << " unless src and " + << "dest images are in the same size and " + << "copying the entire bitmap"; + return; + } + + unsigned char* src_img_data = src_img->image_data(); + unsigned char* dst_img_data = image_data(); + + // crop part of image from src img, scale it in + // bilinear interpolation fashion, and paste it + // on dst img. + BilinearInterpolateScale(src_img_data, src_x, src_y, + src_width, src_height, + src_img->width_, src_img->height_, + dst_img_data, dst_x, dst_y, + dst_width, dst_height, + width_, height_, components); +} + +// static utility function used by DrawImage in bitmap and textures. +// in this function, positions are converted to 4th-quadrant, which +// means origin locates left-up corner. +void Bitmap::BilinearInterpolateScale(const uint8* src_img_data, + int src_x, int src_y, + int src_width, int src_height, + int src_img_width, int src_img_height, + uint8* dest_img_data, + int dest_x, int dest_y, + int dest_width, int dest_height, + int dest_img_width, int dest_img_height, + int components) { + for (int i = 0; i < std::abs(dest_width); i++) { + // x is the iterator of dest_width in dest_img. + // change x to negative if dest_width is negative. + int x = i; + if (dest_width < 0) + x = -i; + + // calculate corresponding coordinate in src_img. + double base_x = i * (std::abs(src_width) - 1) / + static_cast(std::abs(dest_width) - 1); + // base_floor_x is the iterator of src_width in src_img. + // change base_x to negative if src_width is negative. + if (src_width < 0) + base_x = -base_x; + int base_floor_x = static_cast(std::floor(base_x)); + + for (int j = 0; j < std::abs(dest_height); j++) { + // y is the iterator of dest_height in dest_img. + // change y to negative if dest_height is negative. + int y = j; + if (dest_height < 0) + y = -j; + + // calculate coresponding coordinate in src_img. + double base_y = j * (std::abs(src_height) - 1) / + static_cast(std::abs(dest_height) - 1); + // change base_y to negative if src_height is negative. + if (src_height < 0) + base_y = -base_y; + int base_floor_y = static_cast(std::floor(base_y)); + + for (unsigned int c = 0; c < components; c++) { + // if base_x and base_y are integers, which means this point + // exists in src_img, just copy the original values. + if (base_x - base_floor_x < kEpsilon && + base_y - base_floor_y < kEpsilon) { + dest_img_data[((dest_img_height - (y + dest_y) - 1) * + dest_img_width + dest_x + x) * components + c] = + src_img_data[((src_img_height - (base_floor_y + src_y) - 1) * + src_img_width + src_x + base_floor_x) * components + c]; + continue; + } + + // get four nearest neighbors of point (base_x, base_y) from src img. + uint8 src_neighbor_11, src_neighbor_21, + src_neighbor_12, src_neighbor_22; + src_neighbor_11 = src_img_data[((src_img_height - (base_floor_y + + src_y) - 1) * src_img_width + src_x + + base_floor_x) * components + c]; + // if base_x exists in src img. set src_neighbor_21 to src_neighbor_11 + // so the interpolation result would remain src_neighbor_11. + if (base_x - base_floor_x < kEpsilon) + src_neighbor_21 = src_neighbor_11; + else + src_neighbor_21 = src_img_data[((src_img_height - (base_floor_y + + src_y) - 1) * src_img_width + src_x + + base_floor_x + 1) * components + c]; + // if base_y exists in src img. set src_neighbor_12 to src_neighbor_11 + // so the interpolation result would remain src_neighbor_11. + if (base_y - base_floor_y < kEpsilon) + src_neighbor_12 = src_neighbor_11; + else + src_neighbor_12 = src_img_data[((src_img_height - (base_floor_y + + src_y) - 2) * src_img_width + src_x + + base_floor_x) * components + c]; + + if (base_x - base_floor_x < kEpsilon) + src_neighbor_22 = src_neighbor_21; + else if (base_y - base_floor_y < kEpsilon) + src_neighbor_22 = src_neighbor_12; + else + src_neighbor_22 = src_img_data[((src_img_height - (base_floor_y + + src_y) - 2) * src_img_width + src_x + + base_floor_x + 1) * components + c]; + + // calculate interpolated value. + double interpolatedValue = (1 - (base_y - base_floor_y)) * + ((base_x - base_floor_x) * + src_neighbor_21 + + (1 - (base_x - base_floor_x)) * + src_neighbor_11) + + (base_y - base_floor_y) * + ((base_x - base_floor_x) * + src_neighbor_22 + + (1 - (base_x - base_floor_x)) * + src_neighbor_12); + + // assign the nearest integer of interpolatedValue to dest_img_data. + dest_img_data[((dest_img_height - (y + dest_y) - 1) * dest_img_width + + dest_x + x) * components + c] = + static_cast(interpolatedValue + 0.5); + } + } + } +} Bitmap::ImageFileType Bitmap::GetFileTypeFromFilename(const char *filename) { // Convert the filename to lower case for matching. @@ -574,6 +762,105 @@ bool Bitmap::Scale(unsigned int src_width, return true; } +// Adjust boundaries when using DrawImage function in bitmap or texture. +bool Bitmap::AdjustDrawImageBoundary(int* src_x, int* src_y, + int* src_width, int* src_height, + int src_bmp_width, int src_bmp_height, + int* dest_x, int* dest_y, + int* dest_width, int* dest_height, + int dest_bmp_width, int dest_bmp_height) { + // if src or dest rectangle is out of boundaries, do nothing. + if ((*src_x < 0 && *src_x + *src_width <= 0) || + (*src_y < 0 && *src_y + *src_height <= 0) || + (*dest_x < 0 && *dest_x + *dest_width <= 0) || + (*dest_y < 0 && *dest_y + *dest_height <= 0) || + (*src_x >= src_bmp_width && + *src_x + *src_width >= src_bmp_width - 1) || + (*src_y >= src_bmp_height && + *src_y + *src_height >= src_bmp_height - 1) || + (*dest_x >= dest_bmp_width && + *dest_x + *dest_width >= dest_bmp_width - 1) || + (*dest_y >= dest_bmp_height && + *dest_y + *dest_height >= dest_bmp_height - 1)) + return false; + + // if start points are negative. + // check whether src_x is negative. + if (!AdjustDrawImageBoundHelper(src_x, dest_x, + src_width, dest_width, src_bmp_width)) + return false; + // check whether dest_x is negative. + if (!AdjustDrawImageBoundHelper(dest_x, src_x, + dest_width, src_width, dest_bmp_width)) + return false; + // check whether src_y is negative. + if (!AdjustDrawImageBoundHelper(src_y, dest_y, + src_height, dest_height, src_bmp_height)) + return false; + // check whether dest_y is negative. + if (!AdjustDrawImageBoundHelper(dest_y, src_y, + dest_height, src_height, dest_bmp_height)) + return false; + + // check any width or height becomes negative after adjustment. + if (*src_width == 0 || *src_height == 0 || + *dest_width == 0 || *dest_height == 0) { + return false; + } + + return true; +} + +// utility function called in AdjustDrawImageBoundary. +// help to adjust a specific dimension, +// if start point or ending point is out of boundary. +bool Bitmap::AdjustDrawImageBoundHelper(int* src_a, int* dest_a, + int* src_length, int* dest_length, + int src_bmp_length) { + if (*src_length == 0 || *dest_length == 0) + return false; + + // check if start point is out of boundary. + // if src_a < 0, src_length must be positive. + if (*src_a < 0) { + int src_length_delta = 0 - *src_a; + *dest_a = *dest_a + (*dest_length) * src_length_delta / (*src_length); + *dest_length = *dest_length - (*dest_length) * + src_length_delta / (*src_length); + *src_length = *src_length - src_length_delta; + *src_a = 0; + } + // if src_a >= src_bmp_width, src_length must be negative. + if (*src_a >= src_bmp_length) { + int src_length_delta = *src_a - (src_bmp_length - 1); + *dest_a = *dest_a - (*dest_length) * src_length_delta / (*src_length); + *dest_length = *dest_length - (*dest_length) * + src_length_delta / *src_length; + *src_length = *src_length - src_length_delta; + *src_a = src_bmp_length - 1; + } + + if (*src_length == 0 || *dest_length == 0) + return false; + // check whether start point + related length is out of boundary. + // if src_a + src_length > src_bmp_length, src_length must be positive. + if (*src_a + *src_length > src_bmp_length) { + int src_length_delta = *src_length - (src_bmp_length - *src_a); + *dest_length = *dest_length - (*dest_length) * + src_length_delta / (*src_length); + *src_length = *src_length - src_length_delta; + } + // if src_a + src_length < -1, src_length must be negative. + if (*src_a + *src_length < -1) { + int src_length_delta = 0 - (*src_a + *src_length); + *dest_length = *dest_length + (*dest_length) * + src_length_delta / (*src_length); + *src_length = *src_length + src_length_delta; + } + + return true; +} + // Checks that all the alpha values are 1.0 bool Bitmap::CheckAlphaIsOne() const { if (!image_data()) @@ -682,4 +969,8 @@ bool Bitmap::CheckAlphaIsOne() const { return true; } +ObjectBase::Ref Bitmap::Create(ServiceLocator* service_locator) { + return ObjectBase::Ref(new Bitmap(service_locator)); +} + } // namespace o3d diff --git a/o3d/core/cross/bitmap.h b/o3d/core/cross/bitmap.h index 1d16d4f7..60ebb4a 100644 --- a/o3d/core/cross/bitmap.h +++ b/o3d/core/cross/bitmap.h @@ -1,342 +1,439 @@ -/* - * Copyright 2009, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -// This file contains the declaration of Bitmap helper class that can load -// raw 24- and 32-bit bitmaps from popular image formats. The Bitmap class -// also interprets the file format to record the correct OpenGL buffer format. -// -// Trying to keep this class independent from the OpenGL API in case they -// need retargeting later on. - -#ifndef O3D_CORE_CROSS_BITMAP_H_ -#define O3D_CORE_CROSS_BITMAP_H_ - -#include - -#include "base/cross/bits.h" -#include "core/cross/types.h" -#include "core/cross/texture.h" - -class FilePath; - -namespace o3d { - -class MemoryReadStream; -class RawData; - -class Bitmap { - public: - - // We will fail to load images that are bigger than 4kx4k to avoid security - // risks. GPUs don't usually support bigger sizes anyway. - // The biggest bitmap buffer size with these dimensions is: - // 4k x 4k x 4xsizeof(float) x6 x4/3 (x6 for cube maps, x4/3 for mipmaps) - // That makes 2GB, representable in an unsigned int, so we will avoid wraps. - static const unsigned int kMaxImageDimension = 4096; - enum ImageFileType { - UNKNOWN, - TGA, - JPEG, - PNG, - DDS, - }; - - Bitmap() - : image_data_(NULL), - format_(Texture::UNKNOWN_FORMAT), - width_(0), - height_(0), - num_mipmaps_(0), - is_cubemap_(false) {} - ~Bitmap() {} - - static bool CheckImageDimensions(unsigned int width, unsigned int height) { - return width > 0 && height > 0 && - width <= kMaxImageDimension && height < kMaxImageDimension; - } - - // Creates a copy of a bitmap, copying the pixels as well. - // Parameters: - // source: the source bitmap. - void CopyDeepFrom(const Bitmap &source) { - Allocate(source.format_, source.width_, source.height_, - source.num_mipmaps_, source.is_cubemap_); - memcpy(image_data(), source.image_data(), GetTotalSize()); - } - - // Sets the bitmap parameters from another bitmap, stealing the pixel buffer - // from the source bitmap. - // Parameters: - // source: the source bitmap. - void SetFrom(Bitmap *source) { - image_data_.reset(); - format_ = source->format_; - width_ = source->width_; - height_ = source->height_; - num_mipmaps_ = source->num_mipmaps_; - is_cubemap_ = source->is_cubemap_; - image_data_.swap(source->image_data_); - } - - // Allocates an uninitialized bitmap with specified parameters. - // Parameters: - // format: the format of the pixels. - // width: the width of the base image. - // height: the height of the base image. - // num_mipmaps: the number of mip-maps. - // cube_map: true if creating a cube map. - void Allocate(Texture::Format format, - unsigned int width, - unsigned int height, - unsigned int num_mipmaps, - bool cube_map); - - // Allocates a bitmap with initialized parameters. - // data is zero-initialized - void AllocateData() { - image_data_.reset(new unsigned char[GetTotalSize()]); - memset(image_data_.get(), 0, GetTotalSize()); - } - - // Frees the data owned by the bitmap. - void FreeData() { - image_data_.reset(NULL); - } - - // Gets the total size of the bitmap data, counting all faces and mip levels. - unsigned int GetTotalSize() { - return (is_cubemap_ ? 6 : 1) * - GetMipChainSize(width_, height_, format_, num_mipmaps_); - } - - // Computes the number of bytes of a texture pixel buffer. - static unsigned int GetBufferSize(unsigned int width, - unsigned int height, - Texture::Format format); - - // Gets the image data for a given mip-map level and cube map face. - // Parameters: - // level: mip level to get. - // face: face of cube to get. This parameter is ignored if - // this bitmap is not a cube map. - unsigned char *GetMipData(unsigned int level, - TextureCUBE::CubeFace face) const; - - unsigned char *image_data() const { return image_data_.get(); } - Texture::Format format() const { return format_; } - unsigned int width() const { return width_; } - unsigned int height() const { return height_; } - unsigned int num_mipmaps() const { return num_mipmaps_; } - bool is_cubemap() const { return is_cubemap_; } - - // Returns whether or not the dimensions of the bitmap are power-of-two. - bool IsPOT() const { - return ((width_ & (width_ - 1)) == 0) && ((height_ & (height_ - 1)) == 0); - } - - void set_format(Texture::Format format) { format_ = format; } - void set_width(unsigned int n) { width_ = n; } - void set_height(unsigned int n) { height_ = n; } - void set_num_mipmaps(unsigned int n) { num_mipmaps_ = n; } - void set_is_cubemap(bool is_cubemap) { is_cubemap_ = is_cubemap; } - - // Loads a bitmap from a file. - // Parameters: - // filename: the name of the file to load. - // file_type: the type of file to load. If UNKNOWN, the file type will be - // determined from the filename extension, and if it is not a - // known extension, all the loaders will be tried. - // generate_mipmaps: whether or not to generate all the mip-map levels. - bool LoadFromFile(const FilePath &filepath, - ImageFileType file_type, - bool generate_mipmaps); - - // Loads a bitmap from a RawData object. - // Parameters: - // raw_data: contains the bitmap data in one of the known formats - // file_type: the format of the bitmap data. If UNKNOWN, the file type - // will be determined from the extension from raw_data's uri - // and if it is not a known extension, all the loaders will - // be tried. - // generate_mipmaps: whether or not to generate all the mip-map levels. - bool LoadFromRawData(RawData *raw_data, - ImageFileType file_type, - bool generate_mipmaps); - - // Loads a bitmap from a MemoryReadStream. - // Parameters: - // stream: a stream for the bitmap data in one of the known formats - // filename: a filename (or uri) of the original bitmap data - // (may be an empty string) - // file_type: the format of the bitmap data. If UNKNOWN, the file type - // will be determined from the extension of |filename| - // and if it is not a known extension, all the loaders - // will be tried. - // generate_mipmaps: whether or not to generate all the mip-map levels. - bool LoadFromStream(MemoryReadStream *stream, - const String &filename, - ImageFileType file_type, - bool generate_mipmaps); - - bool LoadFromPNGStream(MemoryReadStream *stream, - const String &filename, - bool generate_mipmaps); - - bool LoadFromTGAStream(MemoryReadStream *stream, - const String &filename, - bool generate_mipmaps); - - bool LoadFromDDSStream(MemoryReadStream *stream, - const String &filename, - bool generate_mipmaps); - - bool LoadFromJPEGStream(MemoryReadStream *stream, - const String &filename, - bool generate_mipmaps); - - // Saves to a PNG file. The image must be of the ARGB8 format, be a 2D image - // with no mip-maps (only the base level). - // Parameters: - // filename: the name of the file to into. - // Returns: - // true if successful. - bool SaveToPNGFile(const char* filename); - - // Checks that the alpha channel for the entire bitmap is 1.0 - bool CheckAlphaIsOne() const; - - // Detects the type of image file based on the filename. - static ImageFileType GetFileTypeFromFilename(const char *filename); - // Detects the type of image file based on the mime-type. - static ImageFileType GetFileTypeFromMimeType(const char *mime_type); - - // Adds filler alpha byte (0xff) after every pixel. Assumes buffer was - // allocated with enough storage) - // can convert RGB -> RGBA, BGR -> BGRA, etc. - static void XYZToXYZA(unsigned char *image_data, int pixel_count); - - // Swaps Red and Blue components in the image. - static void RGBAToBGRA(unsigned char *image_data, int pixel_count); - - // Gets the number of mip-maps required for a full chain starting at - // width x height. - static unsigned int GetMipMapCount(unsigned int width, unsigned int height) { - return 1 + base::bits::Log2Floor(std::max(width, height)); - } - - // Gets the smallest power-of-two value that is at least as high as - // dimension. This is the POT dimension used in ScaleUpToPOT. - static unsigned int GetPOTSize(unsigned int dimension) { - return 1 << base::bits::Log2Ceiling(dimension); - } - - // Gets the size of the buffer containing a mip-map chain, given its base - // width, height, format and number of mip-map levels. - static unsigned int GetMipChainSize(unsigned int base_width, - unsigned int base_height, - Texture::Format format, - unsigned int num_mipmaps); - - // Generates mip-map levels for a single image, using the data from the base - // level. - // NOTE: this doesn't work for DXTC, or floating-point images. - // - // Parameters: - // base_width: the width of the base image. - // base_height: the height of the base image. - // format: the format of the data. - // num_mipmaps: the number of mipmaps to generate. - // data: the data containing the base image, and enough space for the - // mip-maps. - static bool GenerateMipmaps(unsigned int base_width, - unsigned int base_height, - Texture::Format format, - unsigned int num_mipmaps, - unsigned char *data); - - // Scales an image up to power-of-two textures, using point filtering. - // NOTE: this doesn't work for DXTC, or floating-point images. - // - // Parameters: - // width: the non-power-of-two width of the original image. - // height: the non-power-of-two height of the original image. - // format: the format of the data. - // src: the data containing the source data of the original image. - // dst: a buffer with enough space for the power-of-two version. Pixels are - // written from the end to the beginning so dst can be the same buffer as - // src. - static bool ScaleUpToPOT(unsigned int width, - unsigned int height, - Texture::Format format, - const unsigned char *src, - unsigned char *dst); - - // Scales an image to an arbitrary size, using point filtering. - // NOTE: this doesn't work for DXTC, or floating-point images. - // - // Parameters: - // src_width: the width of the original image. - // src_height: the height of the original image. - // format: the format of the data. - // src: the data containing the source data of the original image. - // dst_width: the width of the target image. - // dst_height: the height of the target image. - // dst: a buffer with enough space for the target version. Pixels are - // written from the end to the beginning so dst can be the same buffer as - // src if the transformation is an upscaling. - static bool Scale(unsigned int src_width, - unsigned int src_height, - Texture::Format format, - const unsigned char *src, - unsigned int dst_width, - unsigned int dst_height, - unsigned char *dst); - private: - // pointer to the raw bitmap data - scoped_array image_data_; - // format of the texture this is meant to represent. - Texture::Format format_; - // width of the bitmap in pixels. - unsigned int width_; - // height of the bitmap in pixels. - unsigned int height_; - // number of mipmap levels in this texture. - unsigned int num_mipmaps_; - // is this cube-map data - bool is_cubemap_; - - DISALLOW_COPY_AND_ASSIGN(Bitmap); -}; - -} // namespace o3d - -#endif // O3D_CORE_CROSS_BITMAP_H_ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of Bitmap helper class that can load +// raw 24- and 32-bit bitmaps from popular image formats. The Bitmap class +// also interprets the file format to record the correct OpenGL buffer format. +// +// Trying to keep this class independent from the OpenGL API in case they +// need retargeting later on. + +#ifndef O3D_CORE_CROSS_BITMAP_H_ +#define O3D_CORE_CROSS_BITMAP_H_ + +#include + +#include "base/cross/bits.h" +#include "core/cross/types.h" +#include "core/cross/texture.h" + +class FilePath; + +namespace o3d { + +class MemoryReadStream; +class RawData; +class Pack; + +// Bitmap provides an API for basic image operations on bitmap images, +// including scale and crop. The contents of bitmap can be created from +// a RawData object via LoadFromRawData(), and also can be transfered +// to mip of a Texure2D or a specific face of TextureCUBE via methods +// in Texture. + +class Bitmap : public ParamObject { + public: + typedef SmartPointer Ref; + + explicit Bitmap(ServiceLocator* service_locator); + virtual ~Bitmap() {} + + // We will fail to load images that are bigger than 4kx4k to avoid security + // risks. GPUs don't usually support bigger sizes anyway. + // The biggest bitmap buffer size with these dimensions is: + // 4k x 4k x 4xsizeof(float) x6 x4/3 (x6 for cube maps, x4/3 for mipmaps) + // That makes 2GB, representable in an unsigned int, so we will avoid wraps. + static const unsigned int kMaxImageDimension = 4096; + enum ImageFileType { + UNKNOWN, + TGA, + JPEG, + PNG, + DDS, + }; + + static bool CheckImageDimensions(unsigned int width, unsigned int height) { + return width > 0 && height > 0 && + width <= kMaxImageDimension && height < kMaxImageDimension; + } + + // Creates a copy of a bitmap, copying the pixels as well. + // Parameters: + // source: the source bitmap. + void CopyDeepFrom(const Bitmap &source) { + Allocate(source.format_, source.width_, source.height_, + source.num_mipmaps_, source.is_cubemap_); + memcpy(image_data(), source.image_data(), GetTotalSize()); + } + + // Sets the bitmap parameters from another bitmap, stealing the pixel buffer + // from the source bitmap. + // Parameters: + // source: the source bitmap. + void SetFrom(Bitmap *source) { + image_data_.reset(); + format_ = source->format_; + width_ = source->width_; + height_ = source->height_; + num_mipmaps_ = source->num_mipmaps_; + is_cubemap_ = source->is_cubemap_; + image_data_.swap(source->image_data_); + } + + // Allocates an uninitialized bitmap with specified parameters. + // Parameters: + // format: the format of the pixels. + // width: the width of the base image. + // height: the height of the base image. + // num_mipmaps: the number of mip-maps. + // cube_map: true if creating a cube map. + void Allocate(Texture::Format format, + unsigned int width, + unsigned int height, + unsigned int num_mipmaps, + bool cube_map); + + // Allocates a bitmap with initialized parameters. + // data is zero-initialized + void AllocateData() { + image_data_.reset(new unsigned char[GetTotalSize()]); + memset(image_data_.get(), 0, GetTotalSize()); + } + + // Frees the data owned by the bitmap. + void FreeData() { + image_data_.reset(NULL); + } + + // Gets the total size of the bitmap data, counting all faces and mip levels. + unsigned int GetTotalSize() { + return (is_cubemap_ ? 6 : 1) * + GetMipChainSize(width_, height_, format_, num_mipmaps_); + } + + // Computes the number of bytes of a texture pixel buffer. + static unsigned int GetBufferSize(unsigned int width, + unsigned int height, + Texture::Format format); + + // Gets the image data for a given mip-map level and cube map face. + // Parameters: + // level: mip level to get. + // face: face of cube to get. This parameter is ignored if + // this bitmap is not a cube map. + unsigned char *GetMipData(unsigned int level, + TextureCUBE::CubeFace face) const; + + unsigned char *image_data() const { return image_data_.get(); } + Texture::Format format() const { return format_; } + unsigned int width() const { return width_; } + unsigned int height() const { return height_; } + unsigned int num_mipmaps() const { return num_mipmaps_; } + bool is_cubemap() const { return is_cubemap_; } + + // Returns whether or not the dimensions of the bitmap are power-of-two. + bool IsPOT() const { + return ((width_ & (width_ - 1)) == 0) && ((height_ & (height_ - 1)) == 0); + } + + void set_format(Texture::Format format) { format_ = format; } + void set_width(unsigned int n) { width_ = n; } + void set_height(unsigned int n) { height_ = n; } + void set_num_mipmaps(unsigned int n) { num_mipmaps_ = n; } + void set_is_cubemap(bool is_cubemap) { is_cubemap_ = is_cubemap; } + + // Loads a bitmap from a file. + // Parameters: + // filename: the name of the file to load. + // file_type: the type of file to load. If UNKNOWN, the file type will be + // determined from the filename extension, and if it is not a + // known extension, all the loaders will be tried. + // generate_mipmaps: whether or not to generate all the mip-map levels. + bool LoadFromFile(const FilePath &filepath, + ImageFileType file_type, + bool generate_mipmaps); + + // Loads a bitmap from a RawData object. + // Parameters: + // raw_data: contains the bitmap data in one of the known formats + // file_type: the format of the bitmap data. If UNKNOWN, the file type + // will be determined from the extension from raw_data's uri + // and if it is not a known extension, all the loaders will + // be tried. + // generate_mipmaps: whether or not to generate all the mip-map levels. + bool LoadFromRawData(RawData *raw_data, + ImageFileType file_type, + bool generate_mipmaps); + + // Loads a bitmap from a MemoryReadStream. + // Parameters: + // stream: a stream for the bitmap data in one of the known formats + // filename: a filename (or uri) of the original bitmap data + // (may be an empty string) + // file_type: the format of the bitmap data. If UNKNOWN, the file type + // will be determined from the extension of |filename| + // and if it is not a known extension, all the loaders + // will be tried. + // generate_mipmaps: whether or not to generate all the mip-map levels. + bool LoadFromStream(MemoryReadStream *stream, + const String &filename, + ImageFileType file_type, + bool generate_mipmaps); + + bool LoadFromPNGStream(MemoryReadStream *stream, + const String &filename, + bool generate_mipmaps); + + bool LoadFromTGAStream(MemoryReadStream *stream, + const String &filename, + bool generate_mipmaps); + + bool LoadFromDDSStream(MemoryReadStream *stream, + const String &filename, + bool generate_mipmaps); + + bool LoadFromJPEGStream(MemoryReadStream *stream, + const String &filename, + bool generate_mipmaps); + + // Saves to a PNG file. The image must be of the ARGB8 format, be a 2D image + // with no mip-maps (only the base level). + // Parameters: + // filename: the name of the file to into. + // Returns: + // true if successful. + bool SaveToPNGFile(const char* filename); + + // Checks that the alpha channel for the entire bitmap is 1.0 + bool CheckAlphaIsOne() const; + + // Copy pixels from source bitmap. Scales if the width and height of source + // and dest do not match. + // Parameters: + // source_img: source bitmap which would be drawn. + // source_x: x-coordinate of the starting pixel in the source image. + // source_x: y-coordinate of the starting pixel in the source image. + // source_width: width of the source image to draw. + // source_height: Height of the source image to draw. + // dest_x: x-coordinate of the starting pixel in the dest image. + // dest_y: y-coordinate of the starting pixel in the dest image. + // dest_width: width of the dest image to draw. + // dest_height: height of the dest image to draw. + void DrawImage(Bitmap* source_img, int source_x, int source_y, + int source_width, int source_height, + int dest_x, int dest_y, + int dest_width, int dest_height); + + // Crop part of an image from src, scale it to an arbitrary size + // and paste in dest image. Utility function for all DrawImage + // function in bitmap and textures. Scale operation is based on + // bilinear interpolation. + // Note: this doesn't work for DXTC, or floating-point images. + // + // Parameters: + // src: source image which would be copied from. + // src_x: x-coordinate of the starting pixel in the src image. + // src_y: y-coordinate of the starting pixel in the src image. + // src_width: width of the part in src image to be croped. + // src_height: height of the part in src image to be croped. + // src_img_width: width of the src image. + // src_img_height: height of the src image. + // dest: dest image which would be copied to. + // dest_x: x-coordinate of the starting pixel in the dest image. + // dest_y: y-coordinate of the starting pixel in the dest image. + // dest_width: width of the part in dest image to be pasted to. + // dest_height: height of the part in dest image to be pasted to. + // dest_img_width: width of the dest image. + // dest_img_height: height of the src image. + // component: size of each pixel in terms of array element. + // Returns: + // true if crop and scale succeeds. + static void BilinearInterpolateScale(const uint8* src, + int src_x, int src_y, + int src_width, int src_height, + int src_img_width, int src_img_height, + uint8* dest, + int dest_x, int dest_y, + int dest_width, int dest_height, + int dest_img_width, int dest_img_height, + int component); + + // Detects the type of image file based on the filename. + static ImageFileType GetFileTypeFromFilename(const char *filename); + // Detects the type of image file based on the mime-type. + static ImageFileType GetFileTypeFromMimeType(const char *mime_type); + + // Adds filler alpha byte (0xff) after every pixel. Assumes buffer was + // allocated with enough storage) + // can convert RGB -> RGBA, BGR -> BGRA, etc. + static void XYZToXYZA(unsigned char *image_data, int pixel_count); + + // Swaps Red and Blue components in the image. + static void RGBAToBGRA(unsigned char *image_data, int pixel_count); + + // Gets the number of mip-maps required for a full chain starting at + // width x height. + static unsigned int GetMipMapCount(unsigned int width, unsigned int height) { + return 1 + base::bits::Log2Floor(std::max(width, height)); + } + + // Gets the smallest power-of-two value that is at least as high as + // dimension. This is the POT dimension used in ScaleUpToPOT. + static unsigned int GetPOTSize(unsigned int dimension) { + return 1 << base::bits::Log2Ceiling(dimension); + } + + // Gets the size of the buffer containing a mip-map chain, given its base + // width, height, format and number of mip-map levels. + static unsigned int GetMipChainSize(unsigned int base_width, + unsigned int base_height, + Texture::Format format, + unsigned int num_mipmaps); + + // Generates mip-map levels for a single image, using the data from the base + // level. + // NOTE: this doesn't work for DXTC, or floating-point images. + // + // Parameters: + // base_width: the width of the base image. + // base_height: the height of the base image. + // format: the format of the data. + // num_mipmaps: the number of mipmaps to generate. + // data: the data containing the base image, and enough space for the + // mip-maps. + static bool GenerateMipmaps(unsigned int base_width, + unsigned int base_height, + Texture::Format format, + unsigned int num_mipmaps, + unsigned char *data); + + // Scales an image up to power-of-two textures, using point filtering. + // NOTE: this doesn't work for DXTC, or floating-point images. + // + // Parameters: + // width: the non-power-of-two width of the original image. + // height: the non-power-of-two height of the original image. + // format: the format of the data. + // src: the data containing the source data of the original image. + // dst: a buffer with enough space for the power-of-two version. Pixels are + // written from the end to the beginning so dst can be the same buffer as + // src. + static bool ScaleUpToPOT(unsigned int width, + unsigned int height, + Texture::Format format, + const unsigned char *src, + unsigned char *dst); + + // Scales an image to an arbitrary size, using point filtering. + // NOTE: this doesn't work for DXTC, or floating-point images. + // + // Parameters: + // src_width: the width of the original image. + // src_height: the height of the original image. + // format: the format of the data. + // src: the data containing the source data of the original image. + // dst_width: the width of the target image. + // dst_height: the height of the target image. + // dst: a buffer with enough space for the target version. Pixels are + // written from the end to the beginning so dst can be the same buffer as + // src if the transformation is an upscaling. + static bool Scale(unsigned int src_width, + unsigned int src_height, + Texture::Format format, + const unsigned char *src, + unsigned int dst_width, + unsigned int dst_height, + unsigned char *dst); + + // adjust start points and boundaries when using DrawImage data + // in bitmap and textures. + // Parameters: + // src_x: x-coordinate of the starting pixel in the source image. + // src_y: y-coordinate of the starting pixel in the source image. + // src_width: width of the source image to draw. + // src_height: height of the source image to draw. + // src_bmp_width: original width of source bitmap. + // src_bmp_height: original height of source bitmap. + // dest_x: x-coordinate of the starting pixel in the dest image. + // dest_y: y-coordinate of the starting pixel in the dest image. + // dest_width: width of the dest image to draw. + // dest_height: height of the dest image to draw. + // dest_bmp_width: original width of dest bitmap. + // dest_bmp_height: original height of dest bitmap. + // Returns: + // false if src or dest rectangle is out of boundaries. + static bool AdjustDrawImageBoundary(int* src_x, int* src_y, + int* src_width, int* src_height, + int src_bmp_width, int src_bmp_height, + int* dest_x, int* dest_y, + int* dest_width, int* dest_height, + int dest_bmp_width, int dest_bmp_height); + + private: + friend class IClassManager; + static ObjectBase::Ref Create(ServiceLocator* service_locator); + + // pointer to the raw bitmap data + scoped_array image_data_; + // format of the texture this is meant to represent. + Texture::Format format_; + // width of the bitmap in pixels. + unsigned int width_; + // height of the bitmap in pixels. + unsigned int height_; + // number of mipmap levels in this texture. + unsigned int num_mipmaps_; + // is this cube-map data + bool is_cubemap_; + + // utility function used in AdjustDrawImageBoundary. + // It adjusts start point and related measures + // for a specific dimension. + // Parameter: + // src_a: the coordinate which is negative. + // dest_a: same coordinate in the other image. + // src_length: length measure of source image to draw. + // dest_length: length measure of dest image to draw. + // src_bmp_length: length measure of src image. + // Returns: + // true if adjust is successful. + static bool AdjustDrawImageBoundHelper(int* src_a, int* dest_a, + int* src_length, int* dest_length, + int src_bmp_length); + + O3D_DECL_CLASS(Bitmap, ParamObject); + DISALLOW_COPY_AND_ASSIGN(Bitmap); +}; + +} // namespace o3d + +#endif // O3D_CORE_CROSS_BITMAP_H_ diff --git a/o3d/core/cross/bitmap_test.cc b/o3d/core/cross/bitmap_test.cc index 0c3766d..1d832ef 100644 --- a/o3d/core/cross/bitmap_test.cc +++ b/o3d/core/cross/bitmap_test.cc @@ -358,30 +358,30 @@ TEST_F(BitmapTest, LoadTGAFile24bit) { // Load the texture object from a file. String filename = *g_program_path + "/bitmap_test/tga-256x256-24bit.tga"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::TGA, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::XRGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, ktga256x256_24bit_BGRX)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::TGA, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::XRGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, ktga256x256_24bit_BGRX)); } // Loads a 32 bit TGA file, checks it against the known data. TEST_F(BitmapTest, LoadTGAFile32bit) { String filename = *g_program_path + "/bitmap_test/tga-256x256-32bit.tga"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::TGA, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::ARGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, ktga256x256_32bit_BGRA)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::TGA, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::ARGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, ktga256x256_32bit_BGRA)); } // Tries to load a 5kx5k TGA file, which should fail. @@ -391,48 +391,48 @@ TEST_F(BitmapTest, LoadTGAFileTooLarge) { // but bails before reading the actual image bytes. String filename = *g_program_path + "/bitmap_test/5kx5k.tga"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_FALSE(bitmap.LoadFromFile(filepath, Bitmap::TGA, false)); - EXPECT_TRUE(bitmap.image_data() == NULL); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_FALSE(bitmap->LoadFromFile(filepath, Bitmap::TGA, false)); + EXPECT_TRUE(bitmap->image_data() == NULL); } // Loads a JPEG file, checks it against the known data. TEST_F(BitmapTest, LoadJPEGFile) { String filename = *g_program_path + "/bitmap_test/jpeg-256x256.jpg"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::JPEG, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::XRGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kjpg256x256_BGRX)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::JPEG, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::XRGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kjpg256x256_BGRX)); } // Tries to load a 5kx5k JPEG file, which should fail. TEST_F(BitmapTest, LoadJPEGFileTooLarge) { String filename = *g_program_path + "/bitmap_test/5kx5k.jpg"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_FALSE(bitmap.LoadFromFile(filepath, Bitmap::JPEG, false)); - EXPECT_TRUE(bitmap.image_data() == NULL); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_FALSE(bitmap->LoadFromFile(filepath, Bitmap::JPEG, false)); + EXPECT_TRUE(bitmap->image_data() == NULL); } // Loads a 24 bit PNG file, checks it against the known data. TEST_F(BitmapTest, LoadPNGFile24bit) { String filename = *g_program_path + "/bitmap_test/png-256x256-24bit.png"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::PNG, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::XRGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kpng256x256_24bit_BGRX)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::PNG, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::XRGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kpng256x256_24bit_BGRX)); } // Loads a 24 bit interlaced PNG file, checks it against the known data. @@ -440,29 +440,29 @@ TEST_F(BitmapTest, LoadPNGFile24bitInterlaced) { String filename = *g_program_path + "/bitmap_test/png-256x256-24bit-interlaced.png"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::PNG, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::XRGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kpng256x256_24bit_interlaced_BGRX)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::PNG, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::XRGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kpng256x256_24bit_interlaced_BGRX)); } TEST_F(BitmapTest, LoadPNGFile32bit) { String filename = *g_program_path + "/bitmap_test/png-256x256-32bit.png"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::PNG, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::ARGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kpng256x256_32bit_BGRA)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::PNG, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::ARGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kpng256x256_32bit_BGRA)); } // Loads a palettized PNG file, checks it against the known data. @@ -470,15 +470,15 @@ TEST_F(BitmapTest, LoadPNGFile8bitPalette) { String filename = *g_program_path + "/bitmap_test/png-256x256-8bit-palette.png"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::PNG, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::XRGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kpng256x256_8bit_palette_BGRX)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::PNG, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::XRGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kpng256x256_8bit_palette_BGRX)); } // Loads a palettized PNG file, checks it against the known data. @@ -486,15 +486,15 @@ TEST_F(BitmapTest, LoadPNGFile4bitPalette) { String filename = *g_program_path + "/bitmap_test/png-20x14-4bit-palette.png"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::PNG, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::XRGB8, bitmap.format()); - EXPECT_EQ(20, bitmap.width()); - EXPECT_EQ(14, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kpng20x14_4bit_palette_BGRX)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::PNG, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::XRGB8, bitmap->format()); + EXPECT_EQ(20, bitmap->width()); + EXPECT_EQ(14, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kpng20x14_4bit_palette_BGRX)); } @@ -502,9 +502,9 @@ TEST_F(BitmapTest, LoadPNGFile4bitPalette) { TEST_F(BitmapTest, LoadPNGFileTooLarge) { String filename = *g_program_path + "/bitmap_test/5kx5k.png"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_FALSE(bitmap.LoadFromFile(filepath, Bitmap::PNG, false)); - EXPECT_TRUE(bitmap.image_data() == NULL); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_FALSE(bitmap->LoadFromFile(filepath, Bitmap::PNG, false)); + EXPECT_TRUE(bitmap->image_data() == NULL); } // NOTE: Having trouble recognising the alpha channel in a PNG @@ -514,14 +514,14 @@ TEST_F(BitmapTest, LoadPNGFile8bitPaletteAlpha) { String filename = *g_program_path + "/bitmap_test/png-256x256-8bit-palette-alpha.png"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::PNG, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::ARGB8, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::PNG, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::ARGB8, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); } */ @@ -529,15 +529,15 @@ TEST_F(BitmapTest, LoadPNGFile8bitPaletteAlpha) { TEST_F(BitmapTest, LoadDDSFileDXT1) { String filename = *g_program_path + "/bitmap_test/dds-dxt1-256x256.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::DXT1, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kdxt1_256x256)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::DXT1, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kdxt1_256x256)); } // Loads a DXT1 DDS file with alpha, checks the format. @@ -545,15 +545,15 @@ TEST_F(BitmapTest, LoadDDSFileDXT1Alpha) { String filename = *g_program_path + "/bitmap_test/dds-dxt1-256x256-alpha.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::DXT1, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kdxt1_256x256_alpha)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::DXT1, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kdxt1_256x256_alpha)); } // Loads a DXT1 DDS file with mipmaps, checks the format. @@ -561,18 +561,18 @@ TEST_F(BitmapTest, LoadDDSFileDXT1Mipmap) { String filename = *g_program_path + "/bitmap_test/dds-dxt1-256x256-mipmap.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::DXT1, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(9, bitmap.num_mipmaps()); - for (unsigned int i = 0; i < bitmap.num_mipmaps(); ++i) { - EXPECT_TRUE(bitmap.GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::DXT1, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(9, bitmap->num_mipmaps()); + for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { + EXPECT_TRUE(bitmap->GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); } - EXPECT_TRUE(TestBitmapData(bitmap, kdxt1_256x256_mipmap)); + EXPECT_TRUE(TestBitmapData(*bitmap, kdxt1_256x256_mipmap)); } // Loads a DXT3 DDS file, checks the format. @@ -580,15 +580,15 @@ TEST_F(BitmapTest, LoadDDSFileDXT3) { String filename = *g_program_path + "/bitmap_test/dds-dxt3-256x256-alpha.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::DXT3, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kdxt3_256x256_alpha)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::DXT3, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kdxt3_256x256_alpha)); } // Loads a DXT3 DDS file with mipmaps, checks the format. @@ -596,18 +596,18 @@ TEST_F(BitmapTest, LoadDDSFileDXT3Mipmap) { String filename = *g_program_path + "/bitmap_test/dds-dxt3-256x256-mipmap.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::DXT3, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(9, bitmap.num_mipmaps()); - for (unsigned int i = 0; i < bitmap.num_mipmaps(); ++i) { - EXPECT_TRUE(bitmap.GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::DXT3, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(9, bitmap->num_mipmaps()); + for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { + EXPECT_TRUE(bitmap->GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); } - EXPECT_TRUE(TestBitmapData(bitmap, kdxt3_256x256_mipmap)); + EXPECT_TRUE(TestBitmapData(*bitmap, kdxt3_256x256_mipmap)); } // Loads a DXT5 DDS file, checks the format. @@ -615,15 +615,15 @@ TEST_F(BitmapTest, LoadDDSFileDXT5) { String filename = *g_program_path + "/bitmap_test/dds-dxt5-256x256-alpha.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::DXT5, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(1, bitmap.num_mipmaps()); - EXPECT_TRUE(TestBitmapData(bitmap, kdxt5_256x256_alpha)); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::DXT5, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(1, bitmap->num_mipmaps()); + EXPECT_TRUE(TestBitmapData(*bitmap, kdxt5_256x256_alpha)); } // Loads a DXT5 DDS file with mipmaps, checks the format. @@ -631,18 +631,18 @@ TEST_F(BitmapTest, LoadDDSFileDXT5Mipmap) { String filename = *g_program_path + "/bitmap_test/dds-dxt5-256x256-mipmap.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_TRUE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() != NULL); - EXPECT_FALSE(bitmap.is_cubemap()); - EXPECT_EQ(Texture::DXT5, bitmap.format()); - EXPECT_EQ(256, bitmap.width()); - EXPECT_EQ(256, bitmap.height()); - EXPECT_EQ(9, bitmap.num_mipmaps()); - for (unsigned int i = 0; i < bitmap.num_mipmaps(); ++i) { - EXPECT_TRUE(bitmap.GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() != NULL); + EXPECT_FALSE(bitmap->is_cubemap()); + EXPECT_EQ(Texture::DXT5, bitmap->format()); + EXPECT_EQ(256, bitmap->width()); + EXPECT_EQ(256, bitmap->height()); + EXPECT_EQ(9, bitmap->num_mipmaps()); + for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { + EXPECT_TRUE(bitmap->GetMipData(i, TextureCUBE::FACE_POSITIVE_X) != NULL); } - EXPECT_TRUE(TestBitmapData(bitmap, kdxt5_256x256_mipmap)); + EXPECT_TRUE(TestBitmapData(*bitmap, kdxt5_256x256_mipmap)); } // Tries to load a 5kx5k DDS file, which should fail. @@ -652,9 +652,9 @@ TEST_F(BitmapTest, LoadDDSFileTooLarge) { // but bails before reading the actual image bytes. String filename = *g_program_path + "/bitmap_test/5kx5k.dds"; FilePath filepath = UTF8ToFilePath(filename); - Bitmap bitmap; - EXPECT_FALSE(bitmap.LoadFromFile(filepath, Bitmap::DDS, false)); - EXPECT_TRUE(bitmap.image_data() == NULL); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + EXPECT_FALSE(bitmap->LoadFromFile(filepath, Bitmap::DDS, false)); + EXPECT_TRUE(bitmap->image_data() == NULL); } @@ -854,4 +854,421 @@ TEST_F(BitmapTest, ScaleUpToPOT) { EXPECT_EQ(0, memcmp(data.get(), kScaleUPDataPOT, dst_size)); } +static unsigned char kpng_8x4_drawImage[128] = { + // Raw dest image used in drawimage test. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x20, 0x40, 0x80, 0xff, 0x22, 0x44, 0x88, 0xff, + 0x24, 0x48, 0x90, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x10, 0x20, 0x40, 0xff, 0x12, 0x24, 0x48, 0xff, + 0x14, 0x28, 0x50, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x1a, 0x34, 0x68, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_top_left[128] = { + // expected result of drawimage on top left corner of dest image. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x2c, 0x2c, 0x2c, 0xff, 0x2d, 0x2d, 0x2d, 0xff, + 0x2e, 0x2e, 0x2e, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x2a, 0x2a, 0x2a, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x1a, 0x34, 0x68, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x26, 0x26, 0x26, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_top[128] = { + // expected result of drawimage on top bound of dest image. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x20, 0x40, 0x80, 0xff, 0x22, 0x44, 0x88, 0xff, + 0x24, 0x48, 0x90, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x10, 0x20, 0x40, 0xff, 0x12, 0x24, 0x48, 0xff, + 0x2b, 0x2b, 0x2b, 0xff, 0x2c, 0x2c, 0x2c, 0xff, + 0x2d, 0x2d, 0x2d, 0xff, 0x2e, 0x2e, 0x2e, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x27, 0x27, 0x27, 0xff, 0x28, 0x28, 0x28, 0xff, + 0x29, 0x29, 0x29, 0xff, 0x2a, 0x2a, 0x2a, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_top_right[128] = { + // expected result of drawimage on top right corner of dest image. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x20, 0x40, 0x80, 0xff, 0x22, 0x44, 0x88, 0xff, + 0x24, 0x48, 0x90, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x2b, 0x2b, 0x2b, 0xff, + 0x2c, 0x2c, 0x2c, 0xff, 0x2d, 0x2d, 0x2d, 0xff, + 0x10, 0x20, 0x40, 0xff, 0x12, 0x24, 0x48, 0xff, + 0x14, 0x28, 0x50, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x27, 0x27, 0x27, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x23, 0x23, 0x23, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_right[128] = { + // expected result of drawimage on right bound of dest image. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x2b, 0x2b, 0x2b, 0xff, + 0x2c, 0x2c, 0x2c, 0xff, 0x2d, 0x2d, 0x2d, 0xff, + 0x20, 0x40, 0x80, 0xff, 0x22, 0x44, 0x88, 0xff, + 0x24, 0x48, 0x90, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x27, 0x27, 0x27, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x10, 0x20, 0x40, 0xff, 0x12, 0x24, 0x48, 0xff, + 0x14, 0x28, 0x50, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x23, 0x23, 0x23, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x1f, 0x1f, 0x1f, 0xff, + 0x20, 0x20, 0x20, 0xff, 0x21, 0x21, 0x21, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_bottom_right[128] = { + // expected result of drawimage on bottom right corner of dest image. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x34, 0x68, 0xd0, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x27, 0x27, 0x27, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x20, 0x40, 0x80, 0xff, 0x22, 0x44, 0x88, 0xff, + 0x24, 0x48, 0x90, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x23, 0x23, 0x23, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x10, 0x20, 0x40, 0xff, 0x12, 0x24, 0x48, 0xff, + 0x14, 0x28, 0x50, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x1f, 0x1f, 0x1f, 0xff, + 0x20, 0x20, 0x20, 0xff, 0x21, 0x21, 0x21, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_bottom[128] = { + // expected result of drawimage on bottom bound of dest image. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x27, 0x27, 0x27, 0xff, 0x28, 0x28, 0x28, 0xff, + 0x29, 0x29, 0x29, 0xff, 0x2a, 0x2a, 0x2a, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x20, 0x40, 0x80, 0xff, 0x22, 0x44, 0x88, 0xff, + 0x23, 0x23, 0x23, 0xff, 0x24, 0x24, 0x24, 0xff, + 0x25, 0x25, 0x25, 0xff, 0x26, 0x26, 0x26, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x10, 0x20, 0x40, 0xff, 0x12, 0x24, 0x48, 0xff, + 0x1f, 0x1f, 0x1f, 0xff, 0x20, 0x20, 0x20, 0xff, + 0x21, 0x21, 0x21, 0xff, 0x22, 0x22, 0x22, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_bottom_left[128] = { + // expected result of drawimage on bottom left corner of dest image. + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x2a, 0x2a, 0x2a, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x26, 0x26, 0x26, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x20, 0x20, 0x20, 0xff, 0x21, 0x21, 0x21, 0xff, + 0x22, 0x22, 0x22, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x1a, 0x34, 0x68, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x04, 0x08, 0x10, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_left[128] = { + // expected result of drawimage on left bound of dest image. + 0x2c, 0x2c, 0x2c, 0xff, 0x2d, 0x2d, 0x2d, 0xff, + 0x2e, 0x2e, 0x2e, 0xff, 0x36, 0x6c, 0xd8, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x2a, 0x2a, 0x2a, 0xff, 0x26, 0x4c, 0x98, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x26, 0x26, 0x26, 0xff, 0x16, 0x2c, 0x58, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x1a, 0x34, 0x68, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x20, 0x20, 0x20, 0xff, 0x21, 0x21, 0x21, 0xff, + 0x22, 0x22, 0x22, 0xff, 0x06, 0x0c, 0x18, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_scale_up[128] = { + // expected result of scale up from 2x2 to 8x4. + 0x3f, 0x3f, 0x3f, 0xff, 0x48, 0x48, 0x48, 0xff, + 0x51, 0x51, 0x51, 0xff, 0x5a, 0x5a, 0x5a, 0xff, + 0x64, 0x64, 0x64, 0xff, 0x6d, 0x6d, 0x6d, 0xff, + 0x76, 0x76, 0x76, 0xff, 0x7f, 0x7f, 0x7f, 0xff, + 0x2a, 0x2a, 0x2a, 0xff, 0x32, 0x32, 0x32, 0xff, + 0x39, 0x39, 0x39, 0xff, 0x41, 0x41, 0x41, 0xff, + 0x48, 0x48, 0x48, 0xff, 0x50, 0x50, 0x50, 0xff, + 0x57, 0x57, 0x57, 0xff, 0x5f, 0x5f, 0x5f, 0xff, + 0x15, 0x15, 0x15, 0xff, 0x1b, 0x1b, 0x1b, 0xff, + 0x21, 0x21, 0x21, 0xff, 0x27, 0x27, 0x27, 0xff, + 0x2d, 0x2d, 0x2d, 0xff, 0x33, 0x33, 0x33, 0xff, + 0x39, 0x39, 0x39, 0xff, 0x3f, 0x3f, 0x3f, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x04, 0x04, 0x04, 0xff, + 0x09, 0x09, 0x09, 0xff, 0x0d, 0x0d, 0x0d, 0xff, + 0x12, 0x12, 0x12, 0xff, 0x16, 0x16, 0x16, 0xff, + 0x1b, 0x1b, 0x1b, 0xff, 0x1f, 0x1f, 0x1f, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_scale_down[128] = { + // expected result of scale down from 8x8 to 4x4. + 0xa8, 0xa8, 0xa8, 0xff, 0xaf, 0xaf, 0xaf, 0xff, + 0xb6, 0xb6, 0xb6, 0xff, 0xbd, 0xbd, 0xbd, 0xff, + 0x38, 0x70, 0xe0, 0xff, 0x3a, 0x74, 0xe8, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x70, 0x70, 0x70, 0xff, 0x77, 0x77, 0x77, 0xff, + 0x7e, 0x7e, 0x7e, 0xff, 0x85, 0x85, 0x85, 0xff, + 0x28, 0x50, 0xa0, 0xff, 0x2a, 0x54, 0xa8, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x38, 0x38, 0x38, 0xff, 0x3f, 0x3f, 0x3f, 0xff, + 0x46, 0x46, 0x46, 0xff, 0x4d, 0x4d, 0x4d, 0xff, + 0x18, 0x30, 0x60, 0xff, 0x1a, 0x34, 0x68, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x07, 0x07, 0x07, 0xff, + 0x0e, 0x0e, 0x0e, 0xff, 0x15, 0x15, 0x15, 0xff, + 0x08, 0x10, 0x20, 0xff, 0x0a, 0x14, 0x28, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_scale_out[128] = { + // expected result of scale src image larger than dest image. + 0x63, 0x63, 0x63, 0xff, 0x65, 0x65, 0x65, 0xff, + 0x67, 0x67, 0x67, 0xff, 0x69, 0x69, 0x69, 0xff, + 0x6c, 0x6c, 0x6c, 0xff, 0x6e, 0x6e, 0x6e, 0xff, + 0x70, 0x70, 0x70, 0xff, 0x72, 0x72, 0x72, 0xff, + 0x53, 0x53, 0x53, 0xff, 0x55, 0x55, 0x55, 0xff, + 0x57, 0x57, 0x57, 0xff, 0x59, 0x59, 0x59, 0xff, + 0x5c, 0x5c, 0x5c, 0xff, 0x5e, 0x5e, 0x5e, 0xff, + 0x60, 0x60, 0x60, 0xff, 0x62, 0x62, 0x62, 0xff, + 0x43, 0x43, 0x43, 0xff, 0x45, 0x45, 0x45, 0xff, + 0x47, 0x47, 0x47, 0xff, 0x49, 0x49, 0x49, 0xff, + 0x4c, 0x4c, 0x4c, 0xff, 0x4e, 0x4e, 0x4e, 0xff, + 0x50, 0x50, 0x50, 0xff, 0x52, 0x52, 0x52, 0xff, + 0x33, 0x33, 0x33, 0xff, 0x35, 0x35, 0x35, 0xff, + 0x37, 0x37, 0x37, 0xff, 0x39, 0x39, 0x39, 0xff, + 0x3c, 0x3c, 0x3c, 0xff, 0x3e, 0x3e, 0x3e, 0xff, + 0x40, 0x40, 0x40, 0xff, 0x42, 0x42, 0x42, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_flip[128] = { + // expected result of flip src image. + 0x30, 0x60, 0xc0, 0xff, 0x32, 0x64, 0xc8, 0xff, + 0x22, 0x22, 0x22, 0xff, 0x21, 0x21, 0x21, 0xff, + 0x20, 0x20, 0x20, 0xff, 0x1f, 0x1f, 0x1f, 0xff, + 0x3c, 0x78, 0xf0, 0xff, 0x3e, 0x7c, 0xf8, 0xff, + 0x20, 0x40, 0x80, 0xff, 0x22, 0x44, 0x88, 0xff, + 0x26, 0x26, 0x26, 0xff, 0x25, 0x25, 0x25, 0xff, + 0x24, 0x24, 0x24, 0xff, 0x23, 0x23, 0x23, 0xff, + 0x2c, 0x58, 0xb0, 0xff, 0x2e, 0x5c, 0xb8, 0xff, + 0x10, 0x20, 0x40, 0xff, 0x12, 0x24, 0x48, 0xff, + 0x2a, 0x2a, 0x2a, 0xff, 0x29, 0x29, 0x29, 0xff, + 0x28, 0x28, 0x28, 0xff, 0x27, 0x27, 0x27, 0xff, + 0x1c, 0x38, 0x70, 0xff, 0x1e, 0x3c, 0x78, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x02, 0x04, 0x08, 0xff, + 0x2e, 0x2e, 0x2e, 0xff, 0x2d, 0x2d, 0x2d, 0xff, + 0x2c, 0x2c, 0x2c, 0xff, 0x2b, 0x2b, 0x2b, 0xff, + 0x0c, 0x18, 0x30, 0xff, 0x0e, 0x1c, 0x38, 0xff, +}; + +static unsigned char kpng_8x4_drawImage_argb8[128] = { + // expected result of drawimage with rgb8 format. + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xca, 0xca, 0xca, 0x3e, 0xd7, 0xd7, 0xd7, 0x9a, + 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xca, 0xca, 0xca, 0x3e, + 0xe6, 0xe6, 0xe6, 0xdc, 0xfc, 0xfc, 0xfc, 0xfe, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xca, 0xca, 0xca, 0x3e, 0xe6, 0xe6, 0xe6, 0xdc, + 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xc7, 0xc7, 0xc7, 0x83, 0xf4, 0xf4, 0xf4, 0xfb, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, +}; + +// DrawImage from another bitmap on different positions, +// compare with expected results. +TEST_F(BitmapTest, DrawImage) { + // path of dest image. + String fname_dst = *g_program_path + + "/bitmap_test/png-8x4-24bit-drawimage-dest.png"; + + // load three src bitmaps in different sizes from files. + String filename_2x2_src = *g_program_path + + "/bitmap_test/png-2x2-24bit-drawimage-src.png"; + Bitmap::Ref bitmap_2x2_src(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_2x2_src->LoadFromFile(UTF8ToFilePath(filename_2x2_src), + Bitmap::PNG, false)); + + String filename_4x4_src = *g_program_path + + "/bitmap_test/png-4x4-24bit-drawimage-src.png"; + Bitmap::Ref bitmap_4x4_src(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_4x4_src->LoadFromFile(UTF8ToFilePath(filename_4x4_src), + Bitmap::PNG, false)); + + String filename_8x8_src = *g_program_path + + "/bitmap_test/png-8x8-24bit-drawimage-src.png"; + Bitmap::Ref bitmap_8x8_src(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_8x8_src->LoadFromFile(UTF8ToFilePath(filename_8x8_src), + Bitmap::PNG, false)); + + // test draw image on top left boundary. + Bitmap::Ref bitmap_dest_top_left(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_top_left->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + // test whether the raw image is loaded correctly or not. + EXPECT_TRUE(bitmap_dest_top_left->image_data() != NULL); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_top_left, kpng_8x4_drawImage)); + bitmap_dest_top_left->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, -1, -1, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_top_left, + kpng_8x4_drawImage_top_left)); + + // test draw image on top boundary. + Bitmap::Ref bitmap_dest_top(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_top->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_top->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, 2, -2, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_top, kpng_8x4_drawImage_top)); + + // test draw image on top right boundary. + Bitmap::Ref bitmap_dest_top_right(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_top_right->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_top_right->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, 5, -1, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_top_right, + kpng_8x4_drawImage_top_right)); + + // test draw image on right boundary. + Bitmap::Ref bitmap_dest_right(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_right->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_right->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, 5, 0, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_right, kpng_8x4_drawImage_right)); + + // test draw image on bottom right boundary. + Bitmap::Ref bitmap_dest_bottom_right(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_bottom_right->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_bottom_right->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, 5, 1, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_bottom_right, + kpng_8x4_drawImage_bottom_right)); + + // test draw image on bottom boundary. + Bitmap::Ref bitmap_dest_bottom(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_bottom->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_bottom->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, 2, 1, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_bottom, kpng_8x4_drawImage_bottom)); + + // test draw image on bottom left boundary. + Bitmap::Ref bitmap_dest_bottom_left(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_bottom_left->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_bottom_left->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, -1, 1, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_bottom_left, + kpng_8x4_drawImage_bottom_left)); + + // test draw image on left boundary. + Bitmap::Ref bitmap_dest_left(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_left->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_left->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, -1, 0, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_left, kpng_8x4_drawImage_left)); + + // test scale up. + Bitmap::Ref bitmap_dest_scale_up(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_scale_up->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_scale_up->DrawImage(bitmap_2x2_src, 0, 0, 2, 2, 0, 0, 8, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_scale_up, + kpng_8x4_drawImage_scale_up)); + + // test scale down. + Bitmap::Ref bitmap_dest_scale_down(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_scale_down->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_scale_down->DrawImage(bitmap_8x8_src, 0, 0, 8, 8, 0, 0, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_scale_down, + kpng_8x4_drawImage_scale_down)); + + // test scale up to a large size. + Bitmap::Ref bitmap_dest_scale_out(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_scale_out->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_scale_out->DrawImage(bitmap_8x8_src, 0, 0, 8, 8, -2, -4, 12, 12); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_scale_out, + kpng_8x4_drawImage_scale_out)); + + // test flip an image on both x and y cooridnates. + Bitmap::Ref bitmap_dest_flip(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_flip->LoadFromFile(UTF8ToFilePath(fname_dst), + Bitmap::PNG, false)); + bitmap_dest_flip->DrawImage(bitmap_4x4_src, 0, 0, 4, 4, 5, 3, -4, -4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_flip, kpng_8x4_drawImage_flip)); + + // test draw image on argb8 format. + String fname_dst_argb8 = *g_program_path + + "/bitmap_test/" + + "png-8x4-24bit-drawimage-argb8-dest.png"; + Bitmap::Ref bitmap_dest_argb8(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_dest_argb8->LoadFromFile(UTF8ToFilePath(fname_dst_argb8), + Bitmap::PNG, false)); + String fname_src_argb8 = *g_program_path + + "/bitmap_test/" + + "png-4x4-24bit-drawimage-argb8-src.png"; + Bitmap::Ref bitmap_src_argb8(new Bitmap(g_service_locator)); + EXPECT_TRUE(bitmap_src_argb8->LoadFromFile(UTF8ToFilePath(fname_src_argb8), + Bitmap::PNG, false)); + bitmap_dest_argb8->DrawImage(bitmap_src_argb8, 0, 0, 4, 4, 0, 0, 4, 4); + EXPECT_TRUE(TestBitmapData(*bitmap_dest_argb8, kpng_8x4_drawImage_argb8)); +} + } // namespace diff --git a/o3d/core/cross/class_manager.cc b/o3d/core/cross/class_manager.cc index 8f3cfd5..c037a7b 100644 --- a/o3d/core/cross/class_manager.cc +++ b/o3d/core/cross/class_manager.cc @@ -140,6 +140,7 @@ ClassManager::ClassManager(ServiceLocator* service_locator) AddTypedClass(); // Other Objects. + AddTypedClass(); AddTypedClass(); AddTypedClass(); AddTypedClass(); diff --git a/o3d/core/cross/pack.cc b/o3d/core/cross/pack.cc index 96b06de..87c64be8 100644 --- a/o3d/core/cross/pack.cc +++ b/o3d/core/cross/pack.cc @@ -135,14 +135,14 @@ Texture* Pack::CreateTextureFromFile(const String& uri, // TODO: Add support for volume texture when we have code to load // them - Bitmap bitmap; - if (!bitmap.LoadFromFile(filepath, file_type, generate_mipmaps)) { + Bitmap::Ref bitmap(new Bitmap(service_locator())); + if (!bitmap->LoadFromFile(filepath, file_type, generate_mipmaps)) { O3D_ERROR(service_locator()) << "Failed to load bitmap file \"" << uri << "\""; return NULL; } - return CreateTextureFromBitmap(&bitmap, uri); + return CreateTextureFromBitmap(bitmap, uri); } // Creates a Texture object from a file in the current render context format. @@ -198,7 +198,6 @@ Texture* Pack::CreateTextureFromBitmap(Bitmap *bitmap, const String& uri) { return texture.Get(); } - // Creates a Texture object from RawData and allocates // the necessary resources for it. Texture* Pack::CreateTextureFromRawData(RawData *raw_data, @@ -213,14 +212,53 @@ Texture* Pack::CreateTextureFromRawData(RawData *raw_data, DLOG(INFO) << "CreateTextureFromRawData(uri='" << uri << "')"; - Bitmap bitmap; - if (!bitmap.LoadFromRawData(raw_data, Bitmap::UNKNOWN, generate_mips)) { + Bitmap::Ref bitmap(new Bitmap(service_locator())); + if (!bitmap->LoadFromRawData(raw_data, Bitmap::UNKNOWN, generate_mips)) { O3D_ERROR(service_locator()) << "Failed to load bitmap from raw data \"" << uri << "\""; return NULL; } - return CreateTextureFromBitmap(&bitmap, uri); + return CreateTextureFromBitmap(bitmap, uri); +} + +// Create a bitmap object. +Bitmap* Pack::CreateBitmap(int width, int height, + Texture::Format format) { + DCHECK(Bitmap::CheckImageDimensions(width, height)); + + Bitmap::Ref bitmap(new Bitmap(service_locator())); + if (bitmap.IsNull()) { + O3D_ERROR(service_locator()) + << "Failed to create bitmap object."; + return NULL; + } + bitmap->Allocate(format, width, height, 1, false); + if (!bitmap->image_data()) { + O3D_ERROR(service_locator()) + << "Failed to allocate memory for bitmap."; + return NULL; + } + RegisterObject(bitmap); + return bitmap.Get(); +} + +// Create a new bitmap object from rawdata. +Bitmap* Pack::CreateBitmapFromRawData(RawData* raw_data) { + Bitmap::Ref bitmap(new Bitmap(service_locator())); + if (bitmap.IsNull()) { + O3D_ERROR(service_locator()) + << "Failed to create bitmap object."; + return NULL; + } + if (!bitmap->LoadFromRawData(raw_data, Bitmap::UNKNOWN, + false)) { + O3D_ERROR(service_locator()) + << "Failed to load bitmap from raw data."; + return NULL; + } + RegisterObject(bitmap); + return bitmap.Get(); } // Creates a Texture2D object and allocates the necessary resources for it. diff --git a/o3d/core/cross/pack.h b/o3d/core/cross/pack.h index 2628dc0..8832f02 100644 --- a/o3d/core/cross/pack.h +++ b/o3d/core/cross/pack.h @@ -215,6 +215,26 @@ class Pack : public NamedObject { Texture* CreateTextureFromRawData(RawData* raw_data, bool generate_mips); + // Creates a new Bitmap object of the specified size and format and + // reserves the necessary resources for it. + // Paramters: + // width: The width of the bitmap in pixel. + // height: The height of the bitmap in pixel. + // format: The format of the bitmap. + // Returns: + // A pointer to the bitmap obejct. + + Bitmap* CreateBitmap(int width, int height, Texture::Format format); + + // Creates a new Bitmap object from RawData. + // Parameters: + // raw_data: contains the bitmap data in one of the know formats. + // file_type: the format of the bitmap data. If UNKNOW, the file + // type would determined from the extension. + // Returns: + // A pointer to the bitmap object. + Bitmap* CreateBitmapFromRawData(RawData* raw_data); + // Creates a new Texture2D object of the specified size and format and // reserves the necessary resources for it. // Parameters: @@ -404,13 +424,13 @@ class Pack : public NamedObject { } }; - // Helper method - Texture* CreateTextureFromBitmap(Bitmap *bitmap, const String& uri); - IClassManager* class_manager_; ObjectManager* object_manager_; Renderer* renderer_; + // helper function + Texture* CreateTextureFromBitmap(Bitmap *bitmap, const String& uri); + // The set of objects owned by the pack. This container contains all of the // references that force the lifespan of the contained objects to match // or exceed that of the pack. diff --git a/o3d/core/cross/renderer_test.cc b/o3d/core/cross/renderer_test.cc index c52645b..4c4acdd 100644 --- a/o3d/core/cross/renderer_test.cc +++ b/o3d/core/cross/renderer_test.cc @@ -118,14 +118,14 @@ TEST_F(RendererTest, OffScreen) { // TODO: add InitAndDestroyGL and InitAndDestroyMock TEST_F(RendererTest, Creates2DTextureFromBitmap) { - Bitmap bitmap; - bitmap.Allocate(Texture::ARGB8, 16, 32, 2, false); - memset(bitmap.image_data(), 0x34, bitmap.GetTotalSize()); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + bitmap->Allocate(Texture::ARGB8, 16, 32, 2, false); + memset(bitmap->image_data(), 0x34, bitmap->GetTotalSize()); Client client(g_service_locator); client.Init(); - Texture::Ref texture = g_renderer->CreateTextureFromBitmap(&bitmap); + Texture::Ref texture = g_renderer->CreateTextureFromBitmap(bitmap); ASSERT_TRUE(NULL != texture); EXPECT_EQ(Texture::ARGB8, texture->format()); EXPECT_EQ(2, texture->levels()); @@ -137,14 +137,14 @@ TEST_F(RendererTest, Creates2DTextureFromBitmap) { } TEST_F(RendererTest, CreatesCubeTextureFromBitmap) { - Bitmap bitmap; - bitmap.Allocate(Texture::ARGB8, 16, 16, 2, true); - memset(bitmap.image_data(), 0x34, bitmap.GetTotalSize()); + Bitmap::Ref bitmap(new Bitmap(g_service_locator)); + bitmap->Allocate(Texture::ARGB8, 16, 16, 2, true); + memset(bitmap->image_data(), 0x34, bitmap->GetTotalSize()); Client client(g_service_locator); client.Init(); - Texture::Ref texture = g_renderer->CreateTextureFromBitmap(&bitmap); + Texture::Ref texture = g_renderer->CreateTextureFromBitmap(bitmap); ASSERT_TRUE(NULL != texture); EXPECT_EQ(Texture::ARGB8, texture->format()); EXPECT_EQ(2, texture->levels()); diff --git a/o3d/core/cross/texture.cc b/o3d/core/cross/texture.cc index 058dbb4..88897c3 100644 --- a/o3d/core/cross/texture.cc +++ b/o3d/core/cross/texture.cc @@ -89,6 +89,82 @@ Texture2D::~Texture2D() { levels()))); } +void Texture2D::DrawImage(Bitmap* src_img, + int src_x, int src_y, + int src_width, int src_height, + int dst_x, int dst_y, + int dst_width, int dst_height, int dest_mip) { + DCHECK(src_img->image_data()); + + int mip_width = std::max(1, width() >> dest_mip); + int mip_height = std::max(1, height() >> dest_mip); + + // Clip source and destination rectangles to + // source and destination bitmaps. + // if src or dest rectangle is out of boundary, + // do nothing and return. + if (!Bitmap::AdjustDrawImageBoundary(&src_x, &src_y, + &src_width, &src_height, + src_img->width(), src_img->height(), + &dst_x, &dst_y, + &dst_width, &dst_height, + mip_width, mip_height)) + return; + + unsigned int components = 0; + // check formats of source and dest images. + // format of source and dest should be the same. + if (src_img->format() != format()) { + O3D_ERROR(service_locator()) << "DrawImage does not support " + << "different formats."; + return; + } + // if src and dest are in the same size and drawImage is copying + // the entire bitmap on dest image, just perform memcpy. + if (src_x == 0 && src_y == 0 && dst_x == 0 && dst_y == 0 && + src_img->width() == mip_width && src_img->height() == mip_height && + src_width == src_img->width() && src_height == src_img->height() && + dst_width == mip_width && dst_height == mip_height) { + void* data = NULL; + if (!Lock(dest_mip, &data)) + return; + + uint8* mip_data = static_cast(data); + unsigned int size = Bitmap::GetMipChainSize(mip_width, mip_height, + format(), 1); + memcpy(mip_data, src_img->image_data(), size); + this->Unlock(dest_mip); + + return; + } + if (src_img->format() == Texture::XRGB8 || + src_img->format() == Texture::ARGB8) { + components = 4; + } else { + O3D_ERROR(service_locator()) << "DrawImage does not support format: " + << src_img->format() << " unless src and " + << "dest images are in the same size and " + << "copying the entire bitmap"; + return; + } + + void* data = NULL; + if (!Lock(dest_mip, &data)) + return; + + uint8* src_img_data = src_img->image_data(); + uint8* mip_data = static_cast(data); + + Bitmap::BilinearInterpolateScale(src_img_data, src_x, src_y, + src_width, src_height, + src_img->width(), src_img->height(), + mip_data, dst_x, dst_y, + dst_width, dst_height, + mip_width, mip_height, components); + + this->Unlock(dest_mip); +} + ObjectBase::Ref Texture2D::Create(ServiceLocator* service_locator) { return ObjectBase::Ref(); } @@ -164,4 +240,82 @@ ObjectBase::Ref TextureCUBE::Create(ServiceLocator* service_locator) { return ObjectBase::Ref(); } +void TextureCUBE::DrawImage(Bitmap* src_img, + int src_x, int src_y, + int src_width, int src_height, + int dst_x, int dst_y, + int dst_width, int dst_height, + CubeFace dest_face, int dest_mip) { + DCHECK(src_img->image_data()); + + int mip_length = std::max(1, edge_length() >> dest_mip); + + // Clip source and destination rectangles to + // source and destination bitmaps. + // if src or dest rectangle is out of boundary, + // do nothing and return true. + if (!Bitmap::AdjustDrawImageBoundary(&src_x, &src_y, + &src_width, &src_height, + src_img->width(), src_img->height(), + &dst_x, &dst_y, + &dst_width, &dst_height, + mip_length, mip_length)) + return; + + unsigned int components = 0; + // check formats of source and dest images. + // format of source and dest should be the same. + if (src_img->format() != format()) { + O3D_ERROR(service_locator()) << "DrawImage does not support " + << "different formats."; + return; + } + // if src and dest are in the same size and drawImage is copying + // the entire bitmap on dest image, just perform memcpy. + if (src_x == 0 && src_y == 0 && dst_x == 0 && dst_y == 0 && + src_img->width() == mip_length && src_img->height() == mip_length && + src_width == src_img->width() && src_height == src_img->height() && + dst_width == mip_length && dst_height == mip_length) { + // get mip data by lock method. + void* data = NULL; + if (!Lock(dest_face, dest_mip, &data)) + return; + + uint8* mip_data = static_cast(data); + unsigned int size = Bitmap::GetMipChainSize(mip_length, mip_length, + format(), 1); + memcpy(mip_data, src_img->image_data(), size); + this->Unlock(dest_face, dest_mip); + + return; + } + if (src_img->format() == Texture::XRGB8 || + src_img->format() == Texture::ARGB8) { + components = 4; + } else { + O3D_ERROR(service_locator()) << "DrawImage does not support format: " + << src_img->format() << " unless src and " + << "dest images are in the same size and " + << "copying the entire bitmap"; + return; + } + + void* data = NULL; + if (!Lock(dest_face, dest_mip, &data)) { + return; + } + + uint8* src_img_data = src_img->image_data(); + uint8* mip_data = static_cast(data); + + Bitmap::BilinearInterpolateScale(src_img_data, src_x, src_y, + src_width, src_height, + src_img->width(), src_img->height(), + mip_data, dst_x, dst_y, + dst_width, dst_height, + mip_length, mip_length, components); + + this->Unlock(dest_face, dest_mip); +} + } // namespace o3d diff --git a/o3d/core/cross/texture.h b/o3d/core/cross/texture.h index a0808ec..49ff0d2 100644 --- a/o3d/core/cross/texture.h +++ b/o3d/core/cross/texture.h @@ -41,6 +41,7 @@ namespace o3d { class Pack; +class Bitmap; // An abstract class for 2D textures that defines the interface for getting // the dimensions of the texture and number of mipmap levels. @@ -96,6 +97,24 @@ class Texture2D : public Texture { // Reference to the RenderSurface object. virtual RenderSurface::Ref GetRenderSurface(int mip_level, Pack* pack) = 0; + // Copy pixels from source bitmap to certain mip level. + // Scales if the width and height of source and dest do not match. + // Parameters: + // source_img: source bitmap which would be drawn. + // source_x: x-coordinate of the starting pixel in the source image. + // source_y: y-coordinate of the starting pixel in the source image. + // source_width: width of the source image to draw. + // source_height: Height of the source image to draw. + // dest_x: x-coordinate of the starting pixel in the dest image. + // dest_y: y-coordinate of the starting pixel in the dest image. + // dest_width: width of the dest image. + // dest_height: height of the dest image. + // dest_mip: on which mip level the sourceImg would be drawn. + void DrawImage(Bitmap* source_img, int source_x, int source_y, + int source_width, int source_height, + int dest_x, int dest_y, + int dest_width, int dest_height, int dest_mip); + protected: // Returns true if the mip-map level has been locked. bool IsLocked(unsigned int level) { @@ -208,6 +227,25 @@ class TextureCUBE : public Texture { int level, Pack* pack) = 0; + // Copy pixels from source bitmap to certain mip level. + // Scales if the width and height of source and dest do not match. + // Parameters: + // source_img: source bitmap which would be drawn. + // source_x: x-coordinate of the starting pixel in the source image. + // source_y: y-coordinate of the starting pixel in the source image. + // source_width: width of the source image to draw. + // source_height: Height of the source image to draw. + // dest_x: x-coordinate of the starting pixel in the dest image. + // dest_y: y-coordinate of the starting pixel in the dest image. + // dest_width: width of the dest image. + // dest_height: height of the dest image. + // face: on which face the sourceImg would be drawn. + // dest_mip: on which mip level the sourceImg would be drawn. + void DrawImage(Bitmap* source_img, int source_x, int source_y, + int source_width, int source_height, + int dest_x, int dest_y, int dest_width, + int dest_height, CubeFace face, int dest_mip); + protected: // Returns true if the mip-map level has been locked. bool IsLocked(unsigned int level, CubeFace face) { diff --git a/o3d/core/win/d3d9/renderer_d3d9.cc b/o3d/core/win/d3d9/renderer_d3d9.cc index a2b9470..b4c7ac2 100644 --- a/o3d/core/win/d3d9/renderer_d3d9.cc +++ b/o3d/core/win/d3d9/renderer_d3d9.cc @@ -1663,13 +1663,13 @@ Texture2D::Ref RendererD3D9::CreatePlatformSpecificTexture2D( Texture::Format format, int levels, bool enable_render_surfaces) { - Bitmap bitmap; - bitmap.set_format(format); - bitmap.set_width(width); - bitmap.set_height(height); - bitmap.set_num_mipmaps(levels); + Bitmap::Ref bitmap = Bitmap::Ref(new Bitmap(service_locator())); + bitmap->set_format(format); + bitmap->set_width(width); + bitmap->set_height(height); + bitmap->set_num_mipmaps(levels); return Texture2D::Ref(Texture2DD3D9::Create(service_locator(), - &bitmap, + bitmap, this, enable_render_surfaces)); } @@ -1681,14 +1681,14 @@ TextureCUBE::Ref RendererD3D9::CreatePlatformSpecificTextureCUBE( Texture::Format format, int levels, bool enable_render_surfaces) { - Bitmap bitmap; - bitmap.set_format(format); - bitmap.set_width(edge_length); - bitmap.set_height(edge_length); - bitmap.set_num_mipmaps(levels); - bitmap.set_is_cubemap(true); + Bitmap::Ref bitmap = Bitmap::Ref(new Bitmap(service_locator())); + bitmap->set_format(format); + bitmap->set_width(edge_length); + bitmap->set_height(edge_length); + bitmap->set_num_mipmaps(levels); + bitmap->set_is_cubemap(true); return TextureCUBE::Ref(TextureCUBED3D9::Create(service_locator(), - &bitmap, + bitmap, this, enable_render_surfaces)); } diff --git a/o3d/core/win/d3d9/texture_d3d9.cc b/o3d/core/win/d3d9/texture_d3d9.cc index 1e8bad6..7477848 100644 --- a/o3d/core/win/d3d9/texture_d3d9.cc +++ b/o3d/core/win/d3d9/texture_d3d9.cc @@ -231,6 +231,7 @@ Texture2DD3D9::Texture2DD3D9(ServiceLocator* service_locator, enable_render_surfaces), d3d_texture_(tex) { DCHECK(tex); + backing_bitmap_ = Bitmap::Ref(new Bitmap(service_locator)); } // Attempts to create a IDirect3DTexture9 with the given specs. If the creation @@ -263,8 +264,8 @@ Texture2DD3D9* Texture2DD3D9::Create(ServiceLocator* service_locator, resize_to_pot, enable_render_surfaces); - texture->backing_bitmap_.SetFrom(bitmap); - if (texture->backing_bitmap_.image_data()) { + texture->backing_bitmap_->SetFrom(bitmap); + if (texture->backing_bitmap_->image_data()) { for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { if (!texture->UpdateBackedMipLevel(i)) { DLOG(ERROR) << "Failed to upload bitmap to texture."; @@ -275,12 +276,12 @@ Texture2DD3D9* Texture2DD3D9::Create(ServiceLocator* service_locator, mip_height = std::max(1U, mip_height >> 1); } if (!resize_to_pot) - texture->backing_bitmap_.FreeData(); + texture->backing_bitmap_->FreeData(); } else { if (resize_to_pot) { - texture->backing_bitmap_.AllocateData(); - memset(texture->backing_bitmap_.image_data(), 0, - texture->backing_bitmap_.GetTotalSize()); + texture->backing_bitmap_->AllocateData(); + memset(texture->backing_bitmap_->image_data(), 0, + texture->backing_bitmap_->GetTotalSize()); } } @@ -294,11 +295,11 @@ Texture2DD3D9::~Texture2DD3D9() { bool Texture2DD3D9::UpdateBackedMipLevel(unsigned int level) { DCHECK_LT(level, static_cast(levels())); - DCHECK(backing_bitmap_.image_data()); - DCHECK_EQ(backing_bitmap_.width(), width()); - DCHECK_EQ(backing_bitmap_.height(), height()); - DCHECK_EQ(backing_bitmap_.format(), format()); - DCHECK_EQ(backing_bitmap_.num_mipmaps(), levels()); + DCHECK(backing_bitmap_->image_data()); + DCHECK_EQ(backing_bitmap_->width(), width()); + DCHECK_EQ(backing_bitmap_->height(), height()); + DCHECK_EQ(backing_bitmap_->format(), format()); + DCHECK_EQ(backing_bitmap_->num_mipmaps(), levels()); unsigned int mip_width = std::max(1, width() >> level); unsigned int mip_height = std::max(1, height() >> level); @@ -322,7 +323,7 @@ bool Texture2DD3D9::UpdateBackedMipLevel(unsigned int level) { // TODO: check that the returned pitch is what we expect. const unsigned char *mip_data = - backing_bitmap_.GetMipData(level, TextureCUBE::FACE_POSITIVE_X); + backing_bitmap_->GetMipData(level, TextureCUBE::FACE_POSITIVE_X); if (resize_to_pot_) { Bitmap::Scale(mip_width, mip_height, format(), mip_data, rect_width, rect_height, @@ -393,8 +394,8 @@ bool Texture2DD3D9::Lock(int level, void** texture_data) { return false; } if (resize_to_pot_) { - DCHECK(backing_bitmap_.image_data()); - *texture_data = backing_bitmap_.GetMipData(level, + DCHECK(backing_bitmap_->image_data()); + *texture_data = backing_bitmap_->GetMipData(level, TextureCUBE::FACE_POSITIVE_X); locked_levels_ |= 1 << level; return true; @@ -459,7 +460,7 @@ bool Texture2DD3D9::OnResetDevice() { bool resize_to_pot; unsigned int mip_width, mip_height; return HR(CreateTexture2DD3D9(renderer_d3d9, - &backing_bitmap_, + backing_bitmap_, render_surfaces_enabled(), &resize_to_pot, &mip_width, @@ -487,6 +488,7 @@ TextureCUBED3D9::TextureCUBED3D9(ServiceLocator* service_locator, resize_to_pot, enable_render_surfaces), d3d_cube_texture_(tex) { + backing_bitmap_ = Bitmap::Ref(new Bitmap(service_locator)); } // Attempts to create a D3D9 CubeTexture with the given specs. If creation @@ -519,8 +521,8 @@ TextureCUBED3D9* TextureCUBED3D9::Create(ServiceLocator* service_locator, resize_to_pot, enable_render_surfaces); - texture->backing_bitmap_.SetFrom(bitmap); - if (texture->backing_bitmap_.image_data()) { + texture->backing_bitmap_->SetFrom(bitmap); + if (texture->backing_bitmap_->image_data()) { for (int face = 0; face < 6; ++face) { unsigned int mip_edge = edge; for (unsigned int i = 0; i < bitmap->num_mipmaps(); ++i) { @@ -533,12 +535,12 @@ TextureCUBED3D9* TextureCUBED3D9::Create(ServiceLocator* service_locator, } } if (!resize_to_pot) - texture->backing_bitmap_.FreeData(); + texture->backing_bitmap_->FreeData(); } else { if (resize_to_pot) { - texture->backing_bitmap_.AllocateData(); - memset(texture->backing_bitmap_.image_data(), 0, - texture->backing_bitmap_.GetTotalSize()); + texture->backing_bitmap_->AllocateData(); + memset(texture->backing_bitmap_->image_data(), 0, + texture->backing_bitmap_->GetTotalSize()); } } @@ -563,12 +565,12 @@ TextureCUBED3D9::~TextureCUBED3D9() { bool TextureCUBED3D9::UpdateBackedMipLevel(unsigned int level, TextureCUBE::CubeFace face) { DCHECK_LT(level, static_cast(levels())); - DCHECK(backing_bitmap_.image_data()); - DCHECK(backing_bitmap_.is_cubemap()); - DCHECK_EQ(backing_bitmap_.width(), edge_length()); - DCHECK_EQ(backing_bitmap_.height(), edge_length()); - DCHECK_EQ(backing_bitmap_.format(), format()); - DCHECK_EQ(backing_bitmap_.num_mipmaps(), levels()); + DCHECK(backing_bitmap_->image_data()); + DCHECK(backing_bitmap_->is_cubemap()); + DCHECK_EQ(backing_bitmap_->width(), edge_length()); + DCHECK_EQ(backing_bitmap_->height(), edge_length()); + DCHECK_EQ(backing_bitmap_->format(), format()); + DCHECK_EQ(backing_bitmap_->num_mipmaps(), levels()); unsigned int mip_edge = std::max(1, edge_length() >> level); unsigned int rect_edge = mip_edge; @@ -590,7 +592,7 @@ bool TextureCUBED3D9::UpdateBackedMipLevel(unsigned int level, DCHECK(out_rect.pBits); // TODO: check that the returned pitch is what we expect. - const unsigned char *mip_data = backing_bitmap_.GetMipData(level, face); + const unsigned char *mip_data = backing_bitmap_->GetMipData(level, face); if (resize_to_pot_) { Bitmap::Scale(mip_edge, mip_edge, format(), mip_data, rect_edge, rect_edge, @@ -664,8 +666,8 @@ bool TextureCUBED3D9::Lock(CubeFace face, int level, void** texture_data) { return false; } if (resize_to_pot_) { - DCHECK(backing_bitmap_.image_data()); - *texture_data = backing_bitmap_.GetMipData(level, face); + DCHECK(backing_bitmap_->image_data()); + *texture_data = backing_bitmap_->GetMipData(level, face); locked_levels_[face] |= 1 << level; return true; } else { @@ -731,7 +733,7 @@ bool TextureCUBED3D9::OnResetDevice() { bool resize_to_pot; unsigned int mip_edge; return HR(CreateTextureCUBED3D9(renderer_d3d9, - &backing_bitmap_, + backing_bitmap_, render_surfaces_enabled(), &resize_to_pot, &mip_edge, diff --git a/o3d/core/win/d3d9/texture_d3d9.h b/o3d/core/win/d3d9/texture_d3d9.h index fe954a2..8a19913 100644 --- a/o3d/core/win/d3d9/texture_d3d9.h +++ b/o3d/core/win/d3d9/texture_d3d9.h @@ -110,7 +110,7 @@ class Texture2DD3D9 : public Texture2D { CComPtr d3d_texture_; // A bitmap used to back the NPOT textures on POT-only hardware. - Bitmap backing_bitmap_; + Bitmap::Ref backing_bitmap_; DISALLOW_COPY_AND_ASSIGN(Texture2DD3D9); }; @@ -178,7 +178,7 @@ class TextureCUBED3D9 : public TextureCUBE { CComPtr d3d_cube_texture_; // A bitmap used to back the NPOT textures on POT-only hardware. - Bitmap backing_bitmap_; + Bitmap::Ref backing_bitmap_; DISALLOW_COPY_AND_ASSIGN(TextureCUBED3D9); }; -- cgit v1.1