summaryrefslogtreecommitdiffstats
path: root/o3d/core/cross/bitmap.cc
diff options
context:
space:
mode:
authorgspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
committergspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-27 23:15:42 +0000
commit05b47f7a8c5451f858dc220df0e3a97542edace6 (patch)
treea2273d619f0625c9d44d40842845ccce2eac1045 /o3d/core/cross/bitmap.cc
parent5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff)
downloadchromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz
chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't yet build in it's new home. We'll change that shortly. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/core/cross/bitmap.cc')
-rw-r--r--o3d/core/cross/bitmap.cc684
1 files changed, 684 insertions, 0 deletions
diff --git a/o3d/core/cross/bitmap.cc b/o3d/core/cross/bitmap.cc
new file mode 100644
index 0000000..a5feafe
--- /dev/null
+++ b/o3d/core/cross/bitmap.cc
@@ -0,0 +1,684 @@
+/*
+ * 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 image file codec operations for OpenGL texture
+// loading. Trying to keep this class as independent from the OpenGL API in
+// case they need retargeting later on.
+
+// The precompiled header must appear before anything else.
+#include "core/cross/precompile.h"
+
+#include <cstring>
+#include <sys/stat.h>
+#include "core/cross/bitmap.h"
+#include "utils/cross/file_path_utils.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "import/cross/raw_data.h"
+#include "import/cross/memory_buffer.h"
+#include "import/cross/memory_stream.h"
+
+using file_util::OpenFile;
+using file_util::CloseFile;
+using file_util::GetFileSize;
+
+namespace o3d {
+
+// Gets the size of the buffer containing a an image, given its width, height
+// and format.
+unsigned int Bitmap::GetBufferSize(unsigned int width,
+ unsigned int height,
+ Texture::Format format) {
+ DCHECK(CheckImageDimensions(width, height));
+ unsigned int pixels = width * height;
+ switch (format) {
+ case Texture::XRGB8:
+ case Texture::ARGB8:
+ return 4 * sizeof(unsigned char) * pixels; // NOLINT
+ case Texture::ABGR16F:
+ return 4 * sizeof(unsigned short) * pixels; // NOLINT
+ case Texture::R32F:
+ return sizeof(float) * pixels; // NOLINT
+ case Texture::ABGR32F:
+ return 4 * sizeof(float) * pixels; // NOLINT
+ case Texture::DXT1:
+ case Texture::DXT3:
+ case Texture::DXT5: {
+ unsigned int blocks = ((width + 3) / 4) * ((height + 3) / 4);
+ unsigned int bytes_per_block = format == Texture::DXT1 ? 8 : 16;
+ return blocks * bytes_per_block;
+ }
+ case Texture::UNKNOWN_FORMAT:
+ break;
+ }
+ // failed to find a matching format
+ LOG(ERROR) << "Unrecognized Texture format type.";
+ return 0;
+}
+
+// Gets the size of the buffer containing a mip-map chain, given its base
+// width, height, format and number of mip-map levels.
+unsigned int Bitmap::GetMipChainSize(unsigned int base_width,
+ unsigned int base_height,
+ Texture::Format format,
+ unsigned int num_mipmaps) {
+ DCHECK(CheckImageDimensions(base_width, base_height));
+ unsigned int total_size = 0;
+ unsigned int mip_width = base_width;
+ unsigned int mip_height = base_height;
+ for (unsigned int i = 0; i < num_mipmaps; ++i) {
+ total_size += GetBufferSize(mip_width, mip_height, format);
+ mip_width = std::max(1U, mip_width >> 1);
+ mip_height = std::max(1U, mip_height >> 1);
+ }
+ return total_size;
+}
+
+void Bitmap::Allocate(Texture::Format format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int num_mipmaps,
+ bool cube_map) {
+ DCHECK(CheckImageDimensions(width, height));
+ switch (format) {
+ case Texture::XRGB8:
+ case Texture::ARGB8:
+ case Texture::ABGR16F:
+ case Texture::R32F:
+ case Texture::ABGR32F:
+ case Texture::DXT1:
+ case Texture::DXT3:
+ case Texture::DXT5:
+ break;
+ default:
+ DLOG(FATAL) << "Trying to allocate a bitmap with invalid format";
+ break;
+ }
+ DCHECK(!cube_map || (width == height));
+ DCHECK_LE(num_mipmaps, GetMipMapCount(width, height));
+ DCHECK_GT(num_mipmaps, 0);
+
+ format_ = format;
+ width_ = width;
+ height_ = height;
+ num_mipmaps_ = num_mipmaps;
+ is_cubemap_ = cube_map;
+ AllocateData();
+}
+
+unsigned char *Bitmap::GetMipData(unsigned int level,
+ TextureCUBE::CubeFace face) const {
+ DCHECK(level < num_mipmaps_);
+ if (!image_data_.get()) return NULL;
+ unsigned char *data = image_data_.get();
+ if (is_cubemap_) {
+ data += (face - TextureCUBE::FACE_POSITIVE_X) *
+ GetMipChainSize(width_, height_, format_, num_mipmaps_);
+ }
+ return data + GetMipChainSize(width_, height_, format_, level);
+}
+
+
+bool Bitmap::LoadFromStream(MemoryReadStream *stream,
+ const String &filename,
+ ImageFileType file_type,
+ bool generate_mipmaps) {
+ // If we don't know what type to load, try to detect it based on the file
+ // name.
+ if (file_type == UNKNOWN) {
+ file_type = GetFileTypeFromFilename(filename.c_str());
+ }
+
+ switch (file_type) {
+ case TGA:
+ if (LoadFromTGAStream(stream, filename, generate_mipmaps)) return true;
+ break;
+ case DDS:
+ if (LoadFromDDSStream(stream, filename, generate_mipmaps)) return true;
+ break;
+ case PNG:
+ if (LoadFromPNGStream(stream, filename, generate_mipmaps)) return true;
+ break;
+ case JPEG:
+ if (LoadFromJPEGStream(stream, filename, generate_mipmaps)) return true;
+ break;
+ case UNKNOWN:
+ default:
+ break;
+ }
+
+ // At this point we either could not detect the filetype, or possibly
+ // the file extension was incorrect (eg. a JPEG image with a .png suffix)
+ DLOG(INFO) << "Could not detect file type from filename \""
+ << filename << "\". Trying all the loaders.";
+ // We will try all the loaders, one by one, starting by the ones that can
+ // have an early detect based on magic strings. We Seek(0) after each try
+ // since each attempt changes the stream read position.
+ if (LoadFromDDSStream(stream, filename, generate_mipmaps)) return true;
+
+ stream->Seek(0);
+ if (LoadFromPNGStream(stream, filename, generate_mipmaps)) return true;
+
+ stream->Seek(0);
+ if (LoadFromJPEGStream(stream, filename, generate_mipmaps)) return true;
+
+ stream->Seek(0);
+ if (LoadFromTGAStream(stream, filename, generate_mipmaps)) return true;
+ DLOG(ERROR) << "Failed to load image \"" << filename
+ << "\": unknown file type";
+ return false;
+}
+
+// Given an arbitrary bitmap file, load it all into memory and then call our
+// stream loader
+bool Bitmap::LoadFromFile(const FilePath &filepath,
+ ImageFileType file_type,
+ bool generate_mipmaps) {
+ // Open the file
+ String filename = FilePathToUTF8(filepath);
+ FILE *file = OpenFile(filepath, "rb");
+
+ if (!file) {
+ DLOG(ERROR) << "bitmap file not found \"" << filename << "\"";
+ return false;
+ }
+
+ // Determine the file's length
+ int64 file_size64;
+ if (!GetFileSize(filepath, &file_size64)) {
+ DLOG(ERROR) << "error getting bitmap file size \"" << filename << "\"";
+ CloseFile(file);
+ return false;
+ }
+ if (file_size64 > 0xffffffffLL) {
+ DLOG(ERROR) << "bitmap file is too large \"" << filename << "\"";
+ return false;
+ }
+ size_t file_length = file_size64;
+
+ // Load the compressed image data into memory
+ MemoryBuffer<uint8> file_contents(file_length);
+ uint8 *p = file_contents;
+ if (fread(p, file_length, 1, file) != 1) {
+ DLOG(ERROR) << "error reading bitmap file \"" << filename << "\"";
+ return false;
+ }
+
+ // And create the bitmap from a memory stream
+ MemoryReadStream stream(file_contents, file_length);
+ bool result = LoadFromStream(&stream, filename, file_type, generate_mipmaps);
+ CloseFile(file);
+
+ return result;
+}
+
+// Given a RawData object containing image data in one of our known formats,
+// decide which image format it is and call the correct loading function.
+bool Bitmap::LoadFromRawData(RawData *raw_data,
+ ImageFileType file_type,
+ bool generate_mipmaps) {
+ String filename = raw_data->uri();
+
+ // GetData() returns NULL if it, for example, cannot open the temporary data
+ // file. In that case, it invokes the error callback. We just have to be
+ // careful not to dereference it.
+ const uint8* data = raw_data->GetData();
+ if (!data) {
+ return false;
+ }
+
+ MemoryReadStream stream(data, raw_data->GetLength());
+
+ return LoadFromStream(&stream, filename, file_type, generate_mipmaps);
+}
+
+
+Bitmap::ImageFileType Bitmap::GetFileTypeFromFilename(const char *filename) {
+ // Convert the filename to lower case for matching.
+ // NOTE: Surprisingly, "tolower" is not in the std namespace.
+ String name(filename);
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+
+ // Dispatch loading functions based on filename extensions.
+ String::size_type i = name.rfind(".");
+ if (i == String::npos) {
+ DLOG(INFO) << "Could not detect file type for image \""
+ << filename << "\": no extension.";
+ return UNKNOWN;
+ }
+
+ String extension = name.substr(i);
+ if (extension == ".tga") {
+ DLOG(INFO) << "Bitmap Found a TGA file : " << filename;
+ return TGA;
+ } else if (extension == ".dds") {
+ DLOG(INFO) << "Bitmap Found a DDS file : " << filename;
+ return DDS;
+ } else if (extension == ".png") {
+ DLOG(INFO) << "Bitmap Found a PNG file : " << filename;
+ return PNG;
+ } else if (extension == ".jpg" ||
+ extension == ".jpeg" ||
+ extension == ".jpe") {
+ DLOG(INFO) << "Bitmap Found a JPEG file : " << filename;
+ return JPEG;
+ } else {
+ return UNKNOWN;
+ }
+}
+
+struct MimeTypeToFileType {
+ const char *mime_type;
+ Bitmap::ImageFileType file_type;
+};
+
+static const MimeTypeToFileType mime_type_map[] = {
+ {"image/png", Bitmap::PNG},
+ {"image/jpeg", Bitmap::JPEG},
+ // No official MIME type for TGA or DDS.
+};
+
+Bitmap::ImageFileType Bitmap::GetFileTypeFromMimeType(const char *mime_type) {
+ for (unsigned int i = 0; i < arraysize(mime_type_map); ++i) {
+ if (!strcmp(mime_type, mime_type_map[i].mime_type))
+ return mime_type_map[i].file_type;
+ }
+ return Bitmap::UNKNOWN;
+}
+
+void Bitmap::XYZToXYZA(unsigned char *image_data, int pixel_count) {
+ // We do this pixel by pixel, starting from the end to avoid overlapping
+ // problems.
+ for (unsigned int i = pixel_count - 1; i < pixel_count; --i) {
+ image_data[i*4+3] = 0xff;
+ image_data[i*4+2] = image_data[i*3+2];
+ image_data[i*4+1] = image_data[i*3+1];
+ image_data[i*4+0] = image_data[i*3+0];
+ }
+}
+
+void Bitmap::RGBAToBGRA(unsigned char *image_data, int pixel_count) {
+ for (unsigned int i = 0; i < pixel_count; ++i) {
+ unsigned char c = image_data[i*4+0];
+ image_data[i*4+0] = image_data[i*4+2];
+ image_data[i*4+2] = c;
+ }
+}
+
+// Compute a texel, filtered from several source texels. This function assumes
+// minification.
+// Parameters:
+// x: x-coordinate of the destination texel in the destination image
+// y: y-coordinate of the destination texel in the destination image
+// dst_width: width of the destination image
+// dst_height: height of the destination image
+// dst_data: address of the destination image data
+// src_width: width of the source image
+// src_height: height of the source image
+// src_data: address of the source image data
+// components: number of components in the image.
+static void FilterTexel(unsigned int x,
+ unsigned int y,
+ unsigned int dst_width,
+ unsigned int dst_height,
+ unsigned char *dst_data,
+ unsigned int src_width,
+ unsigned int src_height,
+ const unsigned char *src_data,
+ unsigned int components) {
+ DCHECK(Bitmap::CheckImageDimensions(src_width, src_height));
+ DCHECK(Bitmap::CheckImageDimensions(dst_width, dst_height));
+ DCHECK_LE(dst_width, src_width);
+ DCHECK_LE(dst_height, src_height);
+ DCHECK_LE(x, dst_width);
+ DCHECK_LE(y, dst_height);
+ // the texel at (x, y) represents the square of texture coordinates
+ // [x/dst_w, (x+1)/dst_w) x [y/dst_h, (y+1)/dst_h).
+ // This takes contributions from the texels:
+ // [floor(x*src_w/dst_w), ceil((x+1)*src_w/dst_w)-1]
+ // x
+ // [floor(y*src_h/dst_h), ceil((y+1)*src_h/dst_h)-1]
+ // from the previous level.
+ unsigned int src_min_x = (x*src_width)/dst_width;
+ unsigned int src_max_x = ((x+1)*src_width+dst_width-1)/dst_width - 1;
+ unsigned int src_min_y = (y*src_height)/dst_height;
+ unsigned int src_max_y = ((y+1)*src_height+dst_height-1)/dst_height - 1;
+
+ // Find the contribution of source each texel, by computing the coverage of
+ // the destination texel on the source texel. We do all the computations in
+ // fixed point, at a src_height*src_width factor to be able to use ints,
+ // but keep all the precision.
+ // Accumulators need to be 64 bits though, because src_height*src_width can
+ // be 24 bits for a 4kx4k base, to which we need to multiply the component
+ // value which is another 8 bits (and we need to accumulate several of them).
+
+ // NOTE: all of our formats use at most 4 components per pixel.
+ // Instead of dynamically allocating a buffer for each pixel on the heap,
+ // just allocate the worst case on the stack.
+ DCHECK_LE(components, 4);
+ uint64 accum[4] = {0};
+ for (unsigned int src_x = src_min_x; src_x <= src_max_x; ++src_x) {
+ for (unsigned int src_y = src_min_y; src_y <= src_max_y; ++src_y) {
+ // The contribution of a fully covered texel is 1/(m_x*m_y) where m_x is
+ // the x-dimension minification factor (src_width/dst_width) and m_y is
+ // the y-dimenstion minification factor (src_height/dst_height).
+ // If the texel is partially covered (on a border), the contribution is
+ // proportional to the covered area. We compute it as the product of the
+ // covered x-length by the covered y-length.
+
+ unsigned int x_contrib = dst_width;
+ if (src_x * dst_width < x * src_width) {
+ // source texel is across the left border of the footprint of the
+ // destination texel.
+ x_contrib = (src_x + 1) * dst_width - x * src_width;
+ } else if ((src_x + 1) * dst_width > (x+1) * src_width) {
+ // source texel is across the right border of the footprint of the
+ // destination texel.
+ x_contrib = (x+1) * src_width - src_x * dst_width;
+ }
+ DCHECK(x_contrib > 0);
+ DCHECK(x_contrib <= dst_width);
+ unsigned int y_contrib = dst_height;
+ if (src_y * dst_height < y * src_height) {
+ // source texel is across the top border of the footprint of the
+ // destination texel.
+ y_contrib = (src_y + 1) * dst_height - y * src_height;
+ } else if ((src_y + 1) * dst_height > (y+1) * src_height) {
+ // source texel is across the bottom border of the footprint of the
+ // destination texel.
+ y_contrib = (y+1) * src_height - src_y * dst_height;
+ }
+ DCHECK(y_contrib > 0);
+ DCHECK(y_contrib <= dst_height);
+ unsigned int contrib = x_contrib * y_contrib;
+ for (unsigned int c = 0; c < components; ++c) {
+ accum[c] +=
+ contrib * src_data[(src_y * src_width + src_x) * components + c];
+ }
+ }
+ }
+ for (unsigned int c = 0; c < components; ++c) {
+ uint64 value = accum[c] / (src_height * src_width);
+ DCHECK_LE(value, 255);
+ dst_data[(y * dst_width + x) * components + c] = value;
+ }
+}
+
+bool Bitmap::GenerateMipmaps(unsigned int base_width,
+ unsigned int base_height,
+ Texture::Format format,
+ unsigned int num_mipmaps,
+ unsigned char *data) {
+ DCHECK(CheckImageDimensions(base_width, base_height));
+ unsigned int components = 0;
+ switch (format) {
+ case Texture::XRGB8:
+ case Texture::ARGB8:
+ components = 4;
+ break;
+ case Texture::ABGR16F:
+ case Texture::R32F:
+ case Texture::ABGR32F:
+ case Texture::DXT1:
+ case Texture::DXT3:
+ case Texture::DXT5:
+ case Texture::UNKNOWN_FORMAT:
+ DLOG(ERROR) << "Mip-map generation not supported for format: " << format;
+ return false;
+ }
+ DCHECK_GE(std::max(base_width, base_height) >> (num_mipmaps-1), 1);
+ unsigned char *mip_data = data;
+ unsigned int mip_width = base_width;
+ unsigned int mip_height = base_height;
+ for (unsigned int level = 1; level < num_mipmaps; ++level) {
+ unsigned int prev_width = mip_width;
+ unsigned int prev_height = mip_height;
+ unsigned char *prev_data = mip_data;
+ mip_data += components * mip_width * mip_height;
+ DCHECK_EQ(mip_data, data + GetMipChainSize(base_width, base_height, format,
+ level));
+ mip_width = std::max(1U, mip_width >> 1);
+ mip_height = std::max(1U, mip_height >> 1);
+
+ if (mip_width * 2 == prev_width && mip_height * 2 == prev_height) {
+ // Easy case: every texel maps to exactly 4 texels in the previous level.
+ for (unsigned int y = 0; y < mip_height; ++y) {
+ for (unsigned int x = 0; x < mip_width; ++x) {
+ for (unsigned int c = 0; c < components; ++c) {
+ // Average the 4 texels.
+ unsigned int offset = (y*2*prev_width + x*2) * components + c;
+ unsigned int value = prev_data[offset]; // (2x, 2y)
+ value += prev_data[offset+components]; // (2x+1, 2y)
+ value += prev_data[offset+prev_width*components]; // (2x, 2y+1)
+ value +=
+ prev_data[offset+(prev_width+1)*components]; // (2x+1, 2y+1)
+ mip_data[(y*mip_width+x) * components + c] = value/4;
+ }
+ }
+ }
+ } else {
+ for (unsigned int y = 0; y < mip_height; ++y) {
+ for (unsigned int x = 0; x < mip_width; ++x) {
+ FilterTexel(x, y, mip_width, mip_height, mip_data, prev_width,
+ prev_height, prev_data, components);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+// Scales the image using basic point filtering.
+bool Bitmap::ScaleUpToPOT(unsigned int width,
+ unsigned int height,
+ Texture::Format format,
+ const unsigned char *src,
+ unsigned char *dst) {
+ DCHECK(CheckImageDimensions(width, height));
+ unsigned int components = 0;
+ switch (format) {
+ case Texture::XRGB8:
+ case Texture::ARGB8:
+ components = 4;
+ break;
+ case Texture::ABGR16F:
+ case Texture::R32F:
+ case Texture::ABGR32F:
+ case Texture::DXT1:
+ case Texture::DXT3:
+ case Texture::DXT5:
+ case Texture::UNKNOWN_FORMAT:
+ DLOG(ERROR) << "Up-scaling is not supported for format: " << format;
+ return false;
+ }
+ unsigned int pot_width = GetPOTSize(width);
+ unsigned int pot_height = GetPOTSize(height);
+ if (pot_width == width && pot_height == height && src == dst)
+ return true;
+ return Scale(width, height, format, src, pot_width, pot_height, dst);
+}
+
+// Scales the image using basic point filtering.
+bool Bitmap::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) {
+ DCHECK(CheckImageDimensions(src_width, src_height));
+ DCHECK(CheckImageDimensions(dst_width, dst_height));
+ unsigned int components = 0;
+ switch (format) {
+ case Texture::XRGB8:
+ case Texture::ARGB8:
+ components = 4;
+ break;
+ case Texture::ABGR16F:
+ case Texture::R32F:
+ case Texture::ABGR32F:
+ case Texture::DXT1:
+ case Texture::DXT3:
+ case Texture::DXT5:
+ case Texture::UNKNOWN_FORMAT:
+ DLOG(ERROR) << "Up-scaling is not supported for format: " << format;
+ return false;
+ }
+ // Start from the end to be able to do it in place.
+ for (unsigned int y = dst_height - 1; y < dst_height; --y) {
+ // max value for y is dst_height - 1, which makes :
+ // base_y = (2*dst_height - 1) * src_height / (2 * dst_height)
+ // which is < src_height.
+ unsigned int base_y = ((y * 2 + 1) * src_height) / (dst_height * 2);
+ DCHECK_LT(base_y, src_height);
+ for (unsigned int x = dst_width - 1; x < dst_width; --x) {
+ unsigned int base_x = ((x * 2 + 1) * src_width) / (dst_width * 2);
+ DCHECK_LT(base_x, src_width);
+ for (unsigned int c = 0; c < components; ++c) {
+ dst[(y * dst_width + x) * components + c] =
+ src[(base_y * src_width + base_x) * components + c];
+ }
+ }
+ }
+ return true;
+}
+
+// Checks that all the alpha values are 1.0
+bool Bitmap::CheckAlphaIsOne() const {
+ if (!image_data())
+ return false;
+
+ switch (format()) {
+ case Texture::XRGB8:
+ return true;
+ case Texture::ARGB8: {
+ int faces = is_cubemap() ? 6 : 1;
+ for (int face = 0; face < faces; ++face) {
+ for (unsigned int level = 0; level < num_mipmaps(); ++level) {
+ const unsigned char *data = GetMipData(
+ level,
+ static_cast<TextureCUBE::CubeFace>(face)) + 3;
+ const unsigned char* end = data + Bitmap::GetBufferSize(
+ std::max(1U, width() >> level),
+ std::max(1U, height() >> level),
+ format());
+ while (data < end) {
+ if (*data != 255) {
+ return false;
+ }
+ data += 4;
+ }
+ }
+ }
+ break;
+ }
+ case Texture::DXT1: {
+ int faces = is_cubemap() ? 6 : 1;
+ for (int face = 0; face < faces; ++face) {
+ for (unsigned int level = 0; level < num_mipmaps(); ++level) {
+ const unsigned char *data = GetMipData(
+ level,
+ static_cast<TextureCUBE::CubeFace>(face));
+ const unsigned char* end = data + Bitmap::GetBufferSize(
+ std::max(1U, width() >> level),
+ std::max(1U, height() >> level),
+ format());
+ DCHECK((end - data) % 8 == 0);
+ while (data < end) {
+ int color0 = static_cast<int>(data[0]) |
+ static_cast<int>(data[1]) << 8;
+ int color1 = static_cast<int>(data[2]) |
+ static_cast<int>(data[3]) << 8;
+ if (color0 < color1) {
+ return false;
+ }
+ data += 8;
+ }
+ }
+ }
+ break;
+ }
+ case Texture::DXT3:
+ case Texture::DXT5:
+ return false;
+ case Texture::ABGR16F: {
+ int faces = is_cubemap() ? 6 : 1;
+ for (int face = 0; face < faces; ++face) {
+ for (unsigned int level = 0; level < num_mipmaps(); ++level) {
+ const unsigned char *data = GetMipData(
+ level,
+ static_cast<TextureCUBE::CubeFace>(face)) + 6;
+ const unsigned char* end = data + Bitmap::GetBufferSize(
+ std::max(1U, width() >> level),
+ std::max(1U, height() >> level),
+ format());
+ while (data < end) {
+ if (data[0] != 0x00 || data[1] != 0x3C) {
+ return false;
+ }
+ data += 8;
+ }
+ }
+ }
+ break;
+ }
+ case Texture::R32F:
+ return true;
+ case Texture::ABGR32F: {
+ int faces = is_cubemap() ? 6 : 1;
+ for (int face = 0; face < faces; ++face) {
+ for (unsigned int level = 0; level < num_mipmaps(); ++level) {
+ const unsigned char* data = GetMipData(
+ level,
+ static_cast<TextureCUBE::CubeFace>(face)) + 12;
+ const unsigned char* end = data + Bitmap::GetBufferSize(
+ std::max(1U, width() >> level),
+ std::max(1U, height() >> level),
+ format());
+ while (data < end) {
+ if (*(reinterpret_cast<const float*>(data)) != 1.0f) {
+ return false;
+ }
+ data += 16;
+ }
+ }
+ }
+ break;
+ }
+ case Texture::UNKNOWN_FORMAT:
+ return false;
+ }
+ return true;
+}
+
+} // namespace o3d