diff options
author | gman@google.com <gman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-06 04:54:53 +0000 |
---|---|---|
committer | gman@google.com <gman@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-06 04:54:53 +0000 |
commit | e16ce48093cf5287ab0ad577d4c8b05c4734e6d0 (patch) | |
tree | 8af527623c4da1e7917cfe11ab065c02859d8ccb /o3d/core | |
parent | 132899b905b0f1794d7e82e28f9fe77ca6a08282 (diff) | |
download | chromium_src-e16ce48093cf5287ab0ad577d4c8b05c4734e6d0.zip chromium_src-e16ce48093cf5287ab0ad577d4c8b05c4734e6d0.tar.gz chromium_src-e16ce48093cf5287ab0ad577d4c8b05c4734e6d0.tar.bz2 |
oops, forgot these files
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22584 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/core')
-rw-r--r-- | o3d/core/cross/image_utils.cc | 705 | ||||
-rw-r--r-- | o3d/core/cross/image_utils.h | 256 | ||||
-rw-r--r-- | o3d/core/cross/image_utils_test.cc | 306 | ||||
-rw-r--r-- | o3d/core/cross/vertex_source_test.cc | 8 |
4 files changed, 1273 insertions, 2 deletions
diff --git a/o3d/core/cross/image_utils.cc b/o3d/core/cross/image_utils.cc new file mode 100644 index 0000000..09ddf75 --- /dev/null +++ b/o3d/core/cross/image_utils.cc @@ -0,0 +1,705 @@ +
+/*
+ * 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 functions to help with images.
+
+// The precompiled header must appear before anything else.
+#include "core/cross/precompile.h"
+
+#include "core/cross/image_utils.h"
+
+namespace o3d {
+namespace image {
+
+// Computes the size of the buffer containing a an image, given its width,
+// height and format.
+unsigned int ComputeBufferSize(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(uint8) * pixels; // NOLINT
+ case Texture::ABGR16F:
+ return 4 * sizeof(uint16) * 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 ComputeMipChainSize(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 += ComputeBufferSize(mip_width, mip_height, format);
+ mip_width = std::max(1U, mip_width >> 1);
+ mip_height = std::max(1U, mip_height >> 1);
+ }
+ return total_size;
+}
+
+// Scales the image using basic point filtering.
+bool ScaleUpToPOT(unsigned int width,
+ unsigned int height,
+ Texture::Format format,
+ const uint8 *src,
+ uint8 *dst,
+ int dst_pitch) {
+ DCHECK(CheckImageDimensions(width, height));
+ unsigned int components = 0;
+ switch (format) {
+ case Texture::XRGB8:
+ case Texture::ARGB8:
+ case Texture::ABGR16F:
+ case Texture::R32F:
+ case Texture::ABGR32F:
+ break;
+ case Texture::DXT1:
+ case Texture::DXT3:
+ case Texture::DXT5:
+ case Texture::UNKNOWN_FORMAT:
+ DCHECK(false);
+ return false;
+ }
+ unsigned int pot_width = ComputePOTSize(width);
+ unsigned int pot_height = ComputePOTSize(height);
+ if (pot_width == width && pot_height == height && src == dst)
+ return true;
+ return Scale(
+ width, height, format, src, pot_width, pot_height, dst, dst_pitch);
+}
+
+unsigned int GetNumComponentsForFormat(o3d::Texture::Format format) {
+ unsigned int components = 0;
+ switch (format) {
+ case o3d::Texture::XRGB8:
+ case o3d::Texture::ARGB8:
+ components = 4;
+ break;
+ case o3d::Texture::ABGR16F:
+ case o3d::Texture::R32F:
+ case o3d::Texture::ABGR32F:
+ case o3d::Texture::DXT1:
+ case o3d::Texture::DXT3:
+ case o3d::Texture::DXT5:
+ case o3d::Texture::UNKNOWN_FORMAT:
+ break;
+ }
+ return components;
+}
+
+namespace {
+
+static const float kEpsilon = 0.0001f;
+static const float kPi = 3.14159265358979f;
+static const int kFilterSize = 3;
+
+// utility function, round float numbers into 0 to 255 integers.
+uint8 Safe8Round(float f) {
+ f += 0.5f;
+ if (f < 0.0f) {
+ return 0;
+ } else if (!(f < 255.0f)) {
+ return 255;
+ }
+ return static_cast<uint8>(f);
+}
+
+template <typename T>
+void PointScale(
+ unsigned components,
+ const uint8* src,
+ unsigned src_width,
+ unsigned src_height,
+ uint8* dst,
+ int dst_pitch,
+ unsigned dst_width,
+ unsigned dst_height) {
+ const T* use_src = reinterpret_cast<const T*>(src);
+ T* use_dst = reinterpret_cast<T*>(dst);
+ int pitch = dst_pitch / sizeof(*use_src) / components;
+ // 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) {
+ use_dst[(y * pitch + x) * components + c] =
+ use_src[(base_y * src_width + base_x) * components + c];
+ }
+ }
+ }
+}
+
+} // anonymous namespace
+
+// Scales the image using basic point filtering.
+bool Scale(unsigned int src_width,
+ unsigned int src_height,
+ Texture::Format format,
+ const uint8 *src,
+ unsigned int dst_width,
+ unsigned int dst_height,
+ uint8 *dst,
+ int dst_pitch) {
+ DCHECK(CheckImageDimensions(src_width, src_height));
+ DCHECK(CheckImageDimensions(dst_width, dst_height));
+ switch (format) {
+ case Texture::XRGB8:
+ case Texture::ARGB8: {
+ PointScale<uint8>(4, src, src_width, src_height,
+ dst, dst_pitch, dst_width, dst_height);
+ break;
+ }
+ case Texture::ABGR16F: {
+ PointScale<uint16>(4, src, src_width, src_height,
+ dst, dst_pitch, dst_width, dst_height);
+ break;
+ }
+ case Texture::R32F:
+ case Texture::ABGR32F: {
+ PointScale<float>(format == Texture::R32F ? 1 : 4,
+ src, src_width, src_height,
+ dst, dst_pitch, dst_width, dst_height);
+ break;
+ }
+ case Texture::DXT1:
+ case Texture::DXT3:
+ case Texture::DXT5:
+ case Texture::UNKNOWN_FORMAT:
+ DCHECK(false);
+ return false;
+ }
+ return true;
+}
+
+
+namespace {
+
+// utility function called in AdjustDrawImageBoundary.
+// help to adjust a specific dimension,
+// if start point or ending point is out of boundary.
+bool 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;
+}
+
+void LanczosResize1D(const uint8* src, int src_x, int src_y,
+ int width, int height,
+ int src_bmp_width, int src_bmp_height,
+ uint8* out, int dest_pitch,
+ int dest_x, int dest_y,
+ int nwidth,
+ int dest_bmp_width, int dest_bmp_height,
+ bool isWidth, int components) {
+ int pitch = dest_pitch / components;
+ // calculate scale factor and init the weight array for lanczos filter.
+ float scale = fabs(static_cast<float>(width) / nwidth);
+ float support = kFilterSize * scale;
+ scoped_array<float> weight(new float[static_cast<int>(support * 2) + 4]);
+ // we assume width is the dimension we are scaling, and height stays
+ // the same.
+ for (int i = 0; i < abs(nwidth); ++i) {
+ // center is the corresponding coordinate of i in original img.
+ float center = (i + 0.5f) * scale;
+ // boundary of weight array in original img.
+ int xmin = static_cast<int>(floor(center - support));
+ if (xmin < 0) xmin = 0;
+ int xmax = static_cast<int>(ceil(center + support));
+ if (xmax >= abs(width)) xmax = abs(width) - 1;
+
+ // fill up weight array by lanczos filter.
+ float wsum = 0.0;
+ for (int ox = xmin; ox <= xmax; ++ox) {
+ float wtemp;
+ float dx = ox + 0.5f - center;
+ // lanczos filter
+ if (dx <= -kFilterSize || dx >= kFilterSize) {
+ wtemp = 0.0;
+ } else if (dx == 0.0) {
+ wtemp = 1.0f;
+ } else {
+ wtemp = kFilterSize * sinf(kPi * dx) * sinf(kPi / kFilterSize * dx) /
+ (kPi * kPi * dx * dx);
+ }
+
+ weight[ox - xmin] = wtemp;
+ wsum += wtemp;
+ }
+ int wcount = xmax - xmin + 1;
+
+ // Normalize the weights.
+ if (fabs(wsum) > kEpsilon) {
+ for (int k = 0; k < wcount; ++k) {
+ weight[k] /= wsum;
+ }
+ }
+ // Now that we've computed the filter weights for this x-position
+ // of the image, we can apply that filter to all pixels in that
+ // column.
+ // calculate coordinate in new img.
+ int x = i;
+ if (nwidth < 0)
+ x = -1 * x;
+ // lower bound of coordinate in original img.
+ if (width < 0)
+ xmin = -1 * xmin;
+ for (int j = 0; j < abs(height); ++j) {
+ // coordinate in height, same in src and dest img.
+ int base_y = j;
+ if (height < 0)
+ base_y = -1 * base_y;
+ // TODO(yux): fix the vertical flip problem and merge this if-else
+ // statement coz at that time, there would be no need to check
+ // which measure we are scaling.
+ if (isWidth) {
+ const uint8* inrow = src + ((src_bmp_height - (src_y + base_y) - 1) *
+ src_bmp_width + src_x + xmin) * components;
+ uint8* outpix = out + ((dest_bmp_height - (dest_y + base_y) - 1) *
+ pitch + dest_x + x) * components;
+ int step = components;
+ if (width < 0)
+ step = -1 * step;
+ for (int b = 0; b < components; ++b) {
+ float sum = 0.0;
+ for (int k = 0, xk = b; k < wcount; ++k, xk += step)
+ sum += weight[k] * inrow[xk];
+
+ outpix[b] = Safe8Round(sum);
+ }
+ } else {
+ const uint8* inrow = src + (src_x + base_y + (src_bmp_height -
+ (src_y + xmin) - 1) * src_bmp_width) *
+ components;
+ uint8* outpix = out + (dest_x + base_y + (dest_bmp_height -
+ (dest_y + x) - 1) * pitch) * components;
+
+ int step = src_bmp_width * components;
+ if (width < 0)
+ step = -1 * step;
+ for (int b = 0; b < components; ++b) {
+ float sum = 0.0;
+ for (int k = 0, xk = b; k < wcount; ++k, xk -= step)
+ sum += weight[k] * inrow[xk];
+
+ outpix[b] = Safe8Round(sum);
+ }
+ }
+ }
+ }
+}
+
+// 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.
+void FilterTexel(unsigned int x,
+ unsigned int y,
+ unsigned int dst_width,
+ unsigned int dst_height,
+ uint8 *dst_data,
+ int dst_pitch,
+ unsigned int src_width,
+ unsigned int src_height,
+ const uint8 *src_data,
+ int src_pitch,
+ unsigned int components) {
+ DCHECK(image::CheckImageDimensions(src_width, src_height));
+ DCHECK(image::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);
+ DCHECK_LE(src_width, src_pitch);
+ DCHECK_LE(dst_width, dst_pitch);
+
+ src_pitch /= components;
+ dst_pitch /= components;
+ // 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, 4u);
+ 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_pitch + src_x) * components + c];
+ }
+ }
+ }
+ for (unsigned int c = 0; c < components; ++c) {
+ uint64 value = accum[c] / (src_height * src_width);
+ DCHECK_LE(value, 255u);
+ dst_data[(y * dst_pitch + x) * components + c] =
+ static_cast<uint8>(value);
+ }
+}
+
+} // anonymous namespace
+
+// Adjust boundaries when using DrawImage function in bitmap or texture.
+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) {
+ // 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;
+}
+
+void LanczosScale(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_pitch,
+ int dest_x, int dest_y,
+ int dest_width, int dest_height,
+ int dest_img_width, int dest_img_height,
+ int components) {
+ // Scale the image horizontally to a temp buffer.
+ int temp_img_width = abs(dest_width);
+ int temp_img_height = abs(src_height);
+ int temp_width = dest_width;
+ int temp_height = src_height;
+ int temp_x = 0, temp_y = 0;
+ if (temp_width < 0)
+ temp_x = abs(temp_width) - 1;
+ if (temp_height < 0)
+ temp_y = abs(temp_height) - 1;
+
+ scoped_array<uint8> temp(new uint8[temp_img_width *
+ temp_img_height * components]);
+
+ LanczosResize1D(src, src_x, src_y, src_width, src_height,
+ src_img_width, src_img_height,
+ temp.get(), temp_img_width * components,
+ temp_x, temp_y, temp_width,
+ temp_img_width, temp_img_height, true, components);
+
+ // Scale the temp buffer vertically to get the final result.
+ LanczosResize1D(temp.get(), temp_x, temp_y, temp_height, temp_width,
+ temp_img_width, temp_img_height,
+ dest, dest_pitch,
+ dest_x, dest_y, dest_height,
+ dest_img_width, dest_img_height, false, components);
+}
+
+bool GenerateMipmap(unsigned int src_width,
+ unsigned int src_height,
+ Texture::Format format,
+ const uint8 *src_data,
+ int src_pitch,
+ uint8 *dst_data,
+ int dst_pitch) {
+ unsigned int components = GetNumComponentsForFormat(format);
+ if (components == 0) {
+ DLOG(ERROR) << "Mip-map generation not supported for format: " << format;
+ return false;
+ }
+ unsigned int mip_width = std::max(1U, src_width >> 1);
+ unsigned int mip_height = std::max(1U, src_height >> 1);
+
+ if (mip_width * 2 == src_width && mip_height * 2 == src_height) {
+ src_pitch /= components;
+ dst_pitch /= components;
+ // 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 * src_pitch + x * 2) * components + c;
+ unsigned int value = src_data[offset]; // (2x, 2y)
+ value += src_data[offset + components]; // (2x+1, 2y)
+ value += src_data[offset + src_width * components]; // (2x, 2y+1)
+ value +=
+ src_data[offset + (src_width + 1) * components]; // (2x+1, 2y+1)
+ dst_data[(y * dst_pitch + 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, dst_data, dst_pitch,
+ src_width, src_height, src_data, src_pitch, components);
+ }
+ }
+ }
+}
+
+ImageFileType 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;
+ }
+}
+
+ImageFileType GetFileTypeFromMimeType(const char *mime_type) {
+ struct MimeTypeToFileType {
+ const char *mime_type;
+ ImageFileType file_type;
+ };
+
+ static const MimeTypeToFileType mime_type_map[] = {
+ {"image/png", PNG},
+ {"image/jpeg", JPEG},
+ // No official MIME type for TGA or DDS.
+ };
+
+ for (unsigned int i = 0u; i < arraysize(mime_type_map); ++i) {
+ if (!strcmp(mime_type, mime_type_map[i].mime_type))
+ return mime_type_map[i].file_type;
+ }
+ return UNKNOWN;
+}
+
+void XYZToXYZA(uint8 *image_data, int pixel_count) {
+ // We do this pixel by pixel, starting from the end to avoid overlapping
+ // problems.
+ for (int i = pixel_count - 1; i >= 0; --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 RGBAToBGRA(uint8 *image_data, int pixel_count) {
+ for (int i = 0; i < pixel_count; ++i) {
+ uint8 c = image_data[i * 4 + 0];
+ image_data[i * 4 + 0] = image_data[i * 4 + 2];
+ image_data[i * 4 + 2] = c;
+ }
+}
+
+} // namespace image
+} // namespace o3d
+
+
diff --git a/o3d/core/cross/image_utils.h b/o3d/core/cross/image_utils.h new file mode 100644 index 0000000..0ebecb6 --- /dev/null +++ b/o3d/core/cross/image_utils.h @@ -0,0 +1,256 @@ +/*
+ * 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 functions to help with images.
+
+#ifndef O3D_CORE_CROSS_IMAGE_UTILS_H_
+#define O3D_CORE_CROSS_IMAGE_UTILS_H_
+
+#include "core/cross/precompile.h"
+
+#include "base/cross/bits.h"
+#include "core/cross/types.h"
+#include "core/cross/texture_base.h"
+
+namespace o3d {
+namespace image {
+
+// 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.
+const unsigned int kMaxImageDimension = 4096u;
+
+enum ImageFileType {
+ UNKNOWN,
+ TGA,
+ JPEG,
+ PNG,
+ DDS,
+};
+
+unsigned int GetNumComponentsForFormat(Texture::Format format);
+
+inline bool CheckImageDimensions(unsigned int width, unsigned int height) {
+ return width > 0 && height > 0 &&
+ width <= kMaxImageDimension && height <= kMaxImageDimension;
+}
+
+// Gets the number of mip-maps required for a full chain starting at
+// width x height.
+inline unsigned int ComputeMipMapCount(
+ 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.
+inline unsigned int ComputePOTSize(unsigned int dimension) {
+ return 1 << base::bits::Log2Ceiling(dimension);
+}
+
+// Computes one dimension of a mip.
+inline unsigned ComputeMipDimension(int level, unsigned dimension) {
+ unsigned v = dimension >> level;
+ return v > 0 ? v : 1u;
+}
+
+// Computes the size of the buffer containing a mip-map chain, given its base
+// width, height, format and number of mip-map levels.
+unsigned int ComputeMipChainSize(unsigned int base_width,
+ unsigned int base_height,
+ Texture::Format format,
+ unsigned int num_mipmaps);
+
+inline int ComputePitch(Texture::Format format, unsigned width) {
+ if (Texture::IsCompressedFormat(format)) {
+ unsigned blocks_across = (width + 3u) / 4u;
+ unsigned bytes_per_block = format == Texture::DXT1 ? 8u : 16u;
+ return bytes_per_block * blocks_across;
+ } else {
+ return ComputeMipChainSize(width, 1u, format, 1u);
+ }
+}
+
+// Computes the pitch for a bitmap.
+// NOTE: For textures you must get the pitch from the OS.
+inline int ComputeMipPitch(Texture::Format format,
+ int level,
+ unsigned width) {
+ return ComputePitch(format, ComputeMipDimension(level, width));
+}
+
+// Computes the number of bytes of a bitmap pixel buffer.
+unsigned int ComputeBufferSize(unsigned int width,
+ unsigned int height,
+ Texture::Format format);
+
+// 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
+// Lanczos resampling.
+// 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.
+void LanczosScale(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_pitch,
+ 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.
+ImageFileType GetFileTypeFromFilename(const char *filename);
+//
+// Detects the type of image file based on the mime-type.
+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.
+void XYZToXYZA(uint8 *image_data, int pixel_count);
+
+// Swaps Red and Blue components in the image.
+void RGBAToBGRA(uint8 *image_data, int pixel_count);
+
+// Generates a mip-map for 1 level.
+// NOTE: this doesn't work for DXTC, or floating-point images.
+//
+// Parameters:
+// src_width: the width of the source image.
+// src_height: the height of the source image.
+// format: the format of the data.
+// src_data: the data containing the src image.
+// src_pitch: If the format is uncompressed this is the number of bytes
+// per row of pixels. If compressed this value is unused.
+// dst_data: memory for a mip one level smaller then the source.
+// dst_pitch: If the format is uncompressed this is the number of bytes
+// per row of pixels. If compressed this value is unused.
+bool GenerateMipmap(unsigned int src_width,
+ unsigned int src_height,
+ Texture::Format format,
+ const uint8 *src_data,
+ int src_pitch,
+ uint8 *dst_data,
+ int dst_pitch);
+
+// 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.
+// dst_pitch: Number of bytes across 1 row of pixels.
+bool ScaleUpToPOT(unsigned int width,
+ unsigned int height,
+ Texture::Format format,
+ const uint8 *src,
+ uint8 *dst,
+ int dst_pitch);
+
+// 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.
+// dst_pitch: Number of bytes across 1 row of pixels.
+bool Scale(unsigned int src_width,
+ unsigned int src_height,
+ Texture::Format format,
+ const uint8 *src,
+ unsigned int dst_width,
+ unsigned int dst_height,
+ uint8 *dst,
+ int dst_pitch);
+
+// 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.
+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);
+
+} // namespace image
+} // namespace o3d
+
+#endif // O3D_CORE_CROSS_IMAGE_UTILS_H_
+
diff --git a/o3d/core/cross/image_utils_test.cc b/o3d/core/cross/image_utils_test.cc new file mode 100644 index 0000000..5ef6609 --- /dev/null +++ b/o3d/core/cross/image_utils_test.cc @@ -0,0 +1,306 @@ +/*
+ * 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.
+ */
+
+
+#include "core/cross/client.h"
+#include "core/cross/image_utils.h"
+#include "tests/common/win/testing_common.h"
+#include "base/file_path.h"
+#include "utils/cross/file_path_utils.h"
+
+namespace o3d {
+
+class ImageTest : public testing::Test {
+};
+
+TEST_F(ImageTest, CheckImageDimensions) {
+ EXPECT_TRUE(image::CheckImageDimensions(1u, 1u));
+ EXPECT_FALSE(image::CheckImageDimensions(0u, 1u));
+ EXPECT_FALSE(image::CheckImageDimensions(1u, 0u));
+ EXPECT_TRUE(image::CheckImageDimensions(image::kMaxImageDimension,
+ image::kMaxImageDimension));
+ EXPECT_FALSE(image::CheckImageDimensions(0u, image::kMaxImageDimension));
+ EXPECT_FALSE(image::CheckImageDimensions(image::kMaxImageDimension, 0u));
+}
+
+TEST_F(ImageTest, ComputeMipMapCount) {
+ EXPECT_EQ(image::ComputeMipMapCount(1, 1), 1);
+ EXPECT_EQ(image::ComputeMipMapCount(2, 2), 2);
+ EXPECT_EQ(image::ComputeMipMapCount(2, 1), 2);
+ EXPECT_EQ(image::ComputeMipMapCount(256, 1), 9);
+ EXPECT_EQ(image::ComputeMipMapCount(256, 256), 9);
+}
+
+TEST_F(ImageTest, ComputePOTSize) {
+ EXPECT_EQ(image::ComputePOTSize(1), 1);
+ EXPECT_EQ(image::ComputePOTSize(2), 2);
+ EXPECT_EQ(image::ComputePOTSize(3), 4);
+ EXPECT_EQ(image::ComputePOTSize(4), 4);
+ EXPECT_EQ(image::ComputePOTSize(5), 8);
+ EXPECT_EQ(image::ComputePOTSize(7), 8);
+ EXPECT_EQ(image::ComputePOTSize(8), 8);
+ EXPECT_EQ(image::ComputePOTSize(9), 16);
+ EXPECT_EQ(image::ComputePOTSize(128), 128);
+ EXPECT_EQ(image::ComputePOTSize(129), 256);
+ EXPECT_EQ(image::ComputePOTSize(255), 256);
+ EXPECT_EQ(image::ComputePOTSize(4096), 4096);
+}
+
+static const uint8 kScaleUPDataNPOT[] = {
+ // This is a 3x3 image.
+ 0x75, 0x58, 0x7b, 0x76,
+ 0x8a, 0x54, 0x85, 0x6f,
+ 0x93, 0x56, 0x74, 0x7d,
+
+ 0x3f, 0x58, 0x7a, 0x64,
+ 0x7a, 0x90, 0x75, 0x8f,
+ 0xb4, 0x7c, 0x71, 0x6b,
+
+ 0x84, 0x84, 0x85, 0x6c,
+ 0xb1, 0x73, 0x4f, 0x7c,
+ 0x97, 0x87, 0x78, 0xa2,
+};
+
+static const uint8 kScaleUPDataPOT[] = {
+ // This is the 4x4 scaled-up version of the above.
+ 0x75, 0x58, 0x7b, 0x76,
+ 0x8a, 0x54, 0x85, 0x6f,
+ 0x8a, 0x54, 0x85, 0x6f,
+ 0x93, 0x56, 0x74, 0x7d,
+
+ 0x3f, 0x58, 0x7a, 0x64,
+ 0x7a, 0x90, 0x75, 0x8f,
+ 0x7a, 0x90, 0x75, 0x8f,
+ 0xb4, 0x7c, 0x71, 0x6b,
+
+ 0x3f, 0x58, 0x7a, 0x64,
+ 0x7a, 0x90, 0x75, 0x8f,
+ 0x7a, 0x90, 0x75, 0x8f,
+ 0xb4, 0x7c, 0x71, 0x6b,
+
+ 0x84, 0x84, 0x85, 0x6c,
+ 0xb1, 0x73, 0x4f, 0x7c,
+ 0xb1, 0x73, 0x4f, 0x7c,
+ 0x97, 0x87, 0x78, 0xa2,
+};
+
+// Scales up a NPOT texture, compare with expected results.
+TEST_F(ImageTest, ScaleUpToPOT) {
+ const unsigned int kWidth = 3;
+ const unsigned int kHeight = 3;
+ const unsigned int kComponents = 4;
+ const unsigned int kPOTWidth = image::ComputePOTSize(kWidth);
+ ASSERT_EQ(kPOTWidth, 4);
+ const unsigned int kPOTHeight = image::ComputePOTSize(kHeight);
+ ASSERT_EQ(kPOTHeight, 4);
+ const Texture::Format format = Texture::ARGB8;
+ unsigned int src_size =
+ image::ComputeBufferSize(kWidth, kHeight, format);
+ ASSERT_EQ(sizeof(kScaleUPDataNPOT), src_size);
+ unsigned int dst_size =
+ image::ComputeBufferSize(kPOTWidth, kPOTHeight, format);
+ ASSERT_EQ(sizeof(kScaleUPDataPOT), dst_size);
+ scoped_array<uint8> data(new uint8[dst_size]);
+ ASSERT_TRUE(data.get() != NULL);
+ // Check that scaling works when source and destination don't alias
+ image::ScaleUpToPOT(kWidth, kHeight, format, kScaleUPDataNPOT, data.get(),
+ 4 * 4);
+ EXPECT_EQ(0, memcmp(data.get(), kScaleUPDataPOT, dst_size));
+
+ // Check that scaling works when source and destination do alias
+ memset(data.get(), 0, dst_size);
+ memcpy(data.get(), kScaleUPDataNPOT, src_size);
+ image::ScaleUpToPOT(kWidth, kHeight, format, data.get(), data.get(), 4 * 4);
+ EXPECT_EQ(0, memcmp(data.get(), kScaleUPDataPOT, dst_size));
+}
+
+
+// NOTE: untested ffile types are:
+// png grayscale textures
+// dds cube maps
+// dds mipmapped cube maps
+// dds 1D textures
+// dds 3D textures
+
+
+static const uint8 kMipmapDataPOT[] = {
+ // This is a 4x4 image
+ 0x7D, 0xE4, 0x0F, 0xff, 0x71, 0x7B, 0x9C, 0xff,
+ 0xDD, 0xF0, 0x9D, 0xff, 0xFA, 0x08, 0x49, 0xff,
+ 0xEA, 0x28, 0xF6, 0xff, 0x73, 0x10, 0x64, 0xff,
+ 0x8B, 0x36, 0x58, 0xff, 0x7A, 0x3E, 0x21, 0xff,
+ 0x64, 0xCE, 0xB1, 0xff, 0x36, 0x4D, 0xC5, 0xff,
+ 0xF3, 0x99, 0x7E, 0xff, 0x5C, 0x56, 0x1E, 0xff,
+ 0x59, 0x8C, 0x41, 0xff, 0x39, 0x24, 0x1B, 0xff,
+ 0x5D, 0x4D, 0x96, 0xff, 0x5E, 0xF8, 0x8B, 0xff,
+ // Followed by its 2x2 mip level
+ 0x92, 0x65, 0x81, 0xff, 0xb7, 0x5b, 0x57, 0xff,
+ 0x4b, 0x72, 0x74, 0xff, 0x82, 0x8d, 0x6f, 0xff,
+ // Followed by its 1x1 mip level
+ 0x85, 0x6f, 0x6e, 0xff,
+};
+
+// Generates mip-maps from a known power-of-two image, compare with expected
+// results.
+TEST_F(ImageTest, GenerateMipmapsPOT) {
+ const unsigned int kWidth = 4;
+ const unsigned int kHeight = 4;
+ const unsigned int kComponents = 4;
+ const Texture::Format format = Texture::ARGB8;
+ unsigned int mipmaps = image::ComputeMipMapCount(kWidth, kHeight);
+ EXPECT_EQ(3, mipmaps);
+ unsigned int size =
+ image::ComputeMipChainSize(kWidth, kHeight, format, mipmaps);
+ ASSERT_EQ(sizeof(kMipmapDataPOT), size);
+ scoped_array<uint8> data(new uint8[size]);
+ ASSERT_TRUE(data.get() != NULL);
+ // Copy first level into the buffer.
+ unsigned int base_size =
+ image::ComputeMipChainSize(kWidth, kHeight, format, 1);
+ memcpy(data.get(), kMipmapDataPOT, base_size);
+ image::GenerateMipmap(
+ kWidth, kHeight, format,
+ data.get(), image::ComputeMipPitch(format, 0, kWidth),
+ data.get() + image::ComputeMipChainSize(kWidth, kHeight, format, 1),
+ image::ComputeMipPitch(format, 1, kWidth));
+ image::GenerateMipmap(
+ image::ComputeMipDimension(1, kWidth),
+ image::ComputeMipDimension(1, kHeight),
+ format,
+ data.get() + image::ComputeMipChainSize(kWidth, kHeight, format, 1),
+ image::ComputeMipPitch(format, 1, kWidth),
+ data.get() + image::ComputeMipChainSize(kWidth, kHeight, format, 2),
+ image::ComputeMipPitch(format, 2, kWidth));
+ // Check the result.
+ EXPECT_EQ(0, memcmp(data.get(), kMipmapDataPOT, size));
+}
+
+static const uint8 kMipmapDataNPOT[] = {
+ // This is a 7x7 image
+ 0x0d, 0x16, 0x68, 0x1b, 0xe6, 0x09, 0x89, 0x55,
+ 0xda, 0x28, 0x56, 0x55, 0x3e, 0x00, 0x6f, 0x16,
+ 0x98, 0x11, 0x50, 0x72, 0xe7, 0x17, 0x24, 0xca,
+ 0x05, 0xe9, 0x92, 0x43, 0xd6, 0xc4, 0x57, 0xcd,
+ 0x34, 0x9b, 0x86, 0xcf, 0x50, 0x65, 0xc4, 0x83,
+ 0xaf, 0xa3, 0xaa, 0xe3, 0x7c, 0xab, 0x5f, 0x08,
+ 0xc1, 0x2e, 0xd1, 0xe9, 0xa8, 0x2b, 0x56, 0x64,
+ 0x12, 0x74, 0x92, 0x56, 0x30, 0x16, 0xa0, 0x03,
+ 0x5a, 0x3a, 0x88, 0xb9, 0xe8, 0xa3, 0xfd, 0xf6,
+ 0xa1, 0x3b, 0x7b, 0x2d, 0xfd, 0x71, 0xc0, 0x0b,
+ 0x22, 0x31, 0x41, 0x5a, 0x45, 0x6f, 0x6b, 0x1b,
+ 0x10, 0x5a, 0x16, 0x6e, 0x02, 0x89, 0x12, 0xb1,
+ 0x67, 0xfc, 0x43, 0x78, 0xc0, 0x55, 0x59, 0xa3,
+ 0xf8, 0xe2, 0x6b, 0xb7, 0xad, 0x5f, 0x3c, 0x14,
+ 0xe1, 0x0e, 0x84, 0x89, 0x25, 0xa7, 0xea, 0xc6,
+ 0x63, 0x20, 0xf9, 0x84, 0xa1, 0xcd, 0x62, 0x0f,
+ 0x22, 0xab, 0x59, 0xde, 0xbd, 0xfa, 0xab, 0x4d,
+ 0xca, 0x07, 0x85, 0xdf, 0x83, 0x23, 0x80, 0x8b,
+ 0x5e, 0xe4, 0x57, 0x45, 0x81, 0x34, 0x52, 0x65,
+ 0xf0, 0x14, 0x32, 0x33, 0xfe, 0xe4, 0x31, 0x90,
+ 0x15, 0x51, 0x57, 0x89, 0xed, 0xcf, 0x88, 0xc9,
+ 0x7b, 0xbb, 0xc6, 0x41, 0xd5, 0x93, 0x7c, 0x65,
+ 0x39, 0x80, 0x20, 0xa2, 0xe5, 0xca, 0x9b, 0x7e,
+ 0xb2, 0x1f, 0x0d, 0xdc, 0x5c, 0xab, 0x6b, 0x5b,
+ 0xc5, 0x57, 0xc0, 0xd2,
+ // Followed by its 3x3 mip level
+ 0x75, 0x58, 0x7b, 0x76, 0x8a, 0x54, 0x85, 0x6f,
+ 0x93, 0x56, 0x74, 0x7d, 0x3f, 0x58, 0x7a, 0x64,
+ 0x7a, 0x90, 0x75, 0x8f, 0xb4, 0x7c, 0x71, 0x6b,
+ 0x84, 0x84, 0x85, 0x6c, 0xb1, 0x73, 0x4f, 0x7c,
+ 0x97, 0x87, 0x78, 0xa2,
+ // Followed by its 1x1 mip level
+ 0x88, 0x6e, 0x75, 0x7a,
+};
+
+// Generates mip-maps from a known non-power-of-two image, compare with expected
+// results.
+TEST_F(ImageTest, GenerateMipmapsNPOT) {
+ const unsigned int kWidth = 7;
+ const unsigned int kHeight = 7;
+ const unsigned int kComponents = 4;
+ const Texture::Format format = Texture::ARGB8;
+ unsigned int mipmaps = image::ComputeMipMapCount(kWidth, kHeight);
+ EXPECT_EQ(3, mipmaps);
+ unsigned int size =
+ image::ComputeMipChainSize(kWidth, kHeight, format, mipmaps);
+ ASSERT_EQ(sizeof(kMipmapDataNPOT), size);
+ scoped_array<uint8> data(new uint8[size]);
+ ASSERT_TRUE(data.get() != NULL);
+ // Copy first level into the buffer.
+ unsigned int base_size =
+ image::ComputeMipChainSize(kWidth, kHeight, format, 1);
+ memcpy(data.get(), kMipmapDataNPOT, base_size);
+ image::GenerateMipmap(
+ kWidth, kHeight, format,
+ data.get(), image::ComputeMipPitch(format, 0, kWidth),
+ data.get() + image::ComputeMipChainSize(kWidth, kHeight, format, 1),
+ image::ComputeMipPitch(format, 1, kWidth));
+ image::GenerateMipmap(
+ image::ComputeMipDimension(1, kWidth),
+ image::ComputeMipDimension(1, kHeight),
+ format,
+ data.get() + image::ComputeMipChainSize(kWidth, kHeight, format, 1),
+ image::ComputeMipPitch(format, 1, kWidth),
+ data.get() + image::ComputeMipChainSize(kWidth, kHeight, format, 2),
+ image::ComputeMipPitch(format, 2, kWidth));
+ // Check the result.
+ EXPECT_EQ(0, memcmp(data.get(), kMipmapDataNPOT, size));
+}
+
+// Checks that filenames are detected as the correct type.
+TEST_F(ImageTest, GetFileTypeFromFilename) {
+ EXPECT_EQ(image::TGA, image::GetFileTypeFromFilename("foo.tga"));
+ EXPECT_EQ(image::TGA, image::GetFileTypeFromFilename("BAR.TGA"));
+ EXPECT_EQ(image::PNG, image::GetFileTypeFromFilename("foo.png"));
+ EXPECT_EQ(image::PNG, image::GetFileTypeFromFilename("BAR.PNG"));
+ EXPECT_EQ(image::JPEG, image::GetFileTypeFromFilename("foo.jpeg"));
+ EXPECT_EQ(image::JPEG, image::GetFileTypeFromFilename("BAR.JPEG"));
+ EXPECT_EQ(image::JPEG, image::GetFileTypeFromFilename("foo.jpg"));
+ EXPECT_EQ(image::JPEG, image::GetFileTypeFromFilename("BAR.JPG"));
+ EXPECT_EQ(image::JPEG, image::GetFileTypeFromFilename("foo.jpe"));
+ EXPECT_EQ(image::JPEG, image::GetFileTypeFromFilename("BAR.JPE"));
+ EXPECT_EQ(image::DDS, image::GetFileTypeFromFilename("foo.dds"));
+ EXPECT_EQ(image::DDS, image::GetFileTypeFromFilename("BAR.DDS"));
+ EXPECT_EQ(image::UNKNOWN, image::GetFileTypeFromFilename("foo.bar"));
+ EXPECT_EQ(image::UNKNOWN, image::GetFileTypeFromFilename("FOO.BAR"));
+}
+
+// Checks that mime types are detected as the correct type.
+TEST_F(ImageTest, GetFileTypeFromMimeType) {
+ EXPECT_EQ(image::PNG, image::GetFileTypeFromMimeType("image/png"));
+ EXPECT_EQ(image::JPEG, image::GetFileTypeFromMimeType("image/jpeg"));
+ EXPECT_EQ(image::UNKNOWN, image::GetFileTypeFromFilename("text/plain"));
+ EXPECT_EQ(image::UNKNOWN,
+ image::GetFileTypeFromFilename("application/x-123"));
+}
+
+} // namespace
+
diff --git a/o3d/core/cross/vertex_source_test.cc b/o3d/core/cross/vertex_source_test.cc index d190aa0..ad0cdeb 100644 --- a/o3d/core/cross/vertex_source_test.cc +++ b/o3d/core/cross/vertex_source_test.cc @@ -32,12 +32,16 @@ // This file implements unit tests for class VertexSource. -#include "core/cross/client.h" #include "tests/common/win/testing_common.h" #include "core/cross/vertex_source.h" +#include "core/cross/error_status.h" +#include "core/cross/evaluation_counter.h" #include "core/cross/fake_vertex_source.h" #include "core/cross/pointer_utils.h" -#include "core/cross/evaluation_counter.h" +#include "core/cross/buffer.h" +#include "core/cross/object_manager.h" +#include "core/cross/pack.h" +#include "core/cross/service_dependency.h" namespace o3d { |