| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- //
- // Urho3D Engine
- // Copyright (c) 2008-2011 Lasse Öörni
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #include "Precompiled.h"
- #include "File.h"
- #include "Image.h"
- #include "Log.h"
- #include "StringUtils.h"
- #include <cstring>
- #include <ddraw.h>
- #include <stb_image.h>
- #include <stb_image_write.h>
- #include "DebugNew.h"
- Image::Image(const std::string& name) :
- Resource(name),
- mWidth(0),
- mHeight(0),
- mComponents(0)
- {
- }
- Image::Image(int width, int height, unsigned components, const std::string& name) :
- Resource(name),
- mWidth(0),
- mHeight(0),
- mComponents(0)
- {
- setSize(width, height, components);
- }
- Image::Image(int width, int height, unsigned components, unsigned char* pixelData, const std::string& name) :
- Resource(name),
- mWidth(0),
- mHeight(0),
- mComponents(0)
- {
- setSize(width, height, components);
- setData(pixelData);
- }
- Image::~Image()
- {
- }
- void Image::load(Deserializer& source, ResourceCache* cache)
- {
- char header[4];
- // Read first 4 bytes
- source.read(header, 4);
- // First 4 bytes identify DDS compressed format
- if (strncmp(header, "DDS ", 4))
- {
- // Not DDS, use STBImage to load other image formats as uncompressed
- source.seek(0);
- int width, height;
- unsigned components;
- unsigned char* pixelData = loadImageFile(source, width, height, components);
- setSize(width, height, components);
- setData(pixelData);
- freeImageFile(pixelData);
- }
- else
- {
- // DDS compressed format
- DDSURFACEDESC2 ddsd;
- source.read(&ddsd, sizeof(ddsd));
-
- switch (ddsd.ddpfPixelFormat.dwFourCC)
- {
- case FOURCC_DXT1:
- mCompressedFormat = CF_DXT1;
- mComponents = 3;
- break;
-
- case FOURCC_DXT3:
- mCompressedFormat = CF_DXT3;
- mComponents = 4;
- break;
-
- case FOURCC_DXT5:
- mCompressedFormat = CF_DXT5;
- mComponents = 4;
- break;
-
- default:
- EXCEPTION("Unsupported DDS format");
- }
- unsigned dataSize = source.getSize() - source.getPosition();
- mData = new unsigned char[dataSize];
- mWidth = ddsd.dwWidth;
- mHeight = ddsd.dwHeight;
- mCompressedLevels = ddsd.dwMipMapCount;
- if (!mCompressedLevels)
- mCompressedLevels = 1;
-
- setMemoryUse(dataSize);
- source.read(mData.getPtr(), dataSize);
- }
- }
- void Image::setSize(unsigned width, unsigned height, unsigned components)
- {
- if ((width == mWidth) && (height == mHeight) && (components == mComponents))
- return;
-
- if ((width <= 0) || (height <= 0))
- return;
-
- mData = new unsigned char[width * height * components];
- mWidth = width;
- mHeight = height;
- mComponents = components;
- mCompressedFormat = CF_NONE;
- mCompressedLevels = 0;
-
- setMemoryUse(width * height * components);
- }
- void Image::setData(const unsigned char* pixelData)
- {
- memcpy(mData.getPtr(), pixelData, mWidth * mHeight * mComponents);
- }
- void Image::saveBMP(const std::string& fileName)
- {
- if (!checkDirectoryAccess(getPath(fileName)))
- SAFE_EXCEPTION("Access denied to " + fileName);
-
- if (isCompressed())
- SAFE_EXCEPTION("Can not save compressed image to BMP");
-
- if (mData)
- stbi_write_bmp(fileName.c_str(), mWidth, mHeight, mComponents, mData.getPtr());
- }
- void Image::saveTGA(const std::string& fileName)
- {
- if (!checkDirectoryAccess(getPath(fileName)))
- SAFE_EXCEPTION("Access denied to " + fileName);
-
- if (isCompressed())
- SAFE_EXCEPTION("Can not save compressed image to TGA");
-
- if (mData)
- stbi_write_tga(fileName.c_str(), mWidth, mHeight, mComponents, mData.getPtr());
- }
- unsigned char* Image::loadImageFile(Deserializer& source, int& width, int& height, unsigned& components)
- {
- unsigned dataSize = source.getSize();
-
- SharedArrayPtr<unsigned char> buffer(new unsigned char[dataSize]);
- source.read(buffer.getPtr(), dataSize);
-
- unsigned char* pixelData = stbi_load_from_memory(buffer.getPtr(), dataSize, &width, &height, (int *)&components, 0);
- if (!pixelData)
- EXCEPTION("Could not load image: " + std::string(stbi_failure_reason()));
-
- return pixelData;
- }
- void Image::freeImageFile(unsigned char* pixelData)
- {
- if (!pixelData)
- return;
- stbi_image_free(pixelData);
- }
- SharedPtr<Image> Image::getNextLevel() const
- {
- if (isCompressed())
- EXCEPTION("Can not generate mip level from compressed data");
- if ((mComponents < 1) || (mComponents > 4))
- EXCEPTION("Illegal number of image components for mip level generation");
-
- int widthOut = mWidth / 2;
- int heightOut = mHeight / 2;
-
- if (widthOut < 1)
- widthOut = 1;
- if (heightOut < 1)
- heightOut = 1;
-
- SharedPtr<Image> mipImage(new Image(widthOut, heightOut, mComponents));
-
- const unsigned char* pixelDataIn = mData.getPtr();
- unsigned char* pixelDataOut = mipImage->mData.getPtr();
-
- // 1D case
- if ((mHeight == 1) || (mWidth == 1))
- {
- // Loop using the larger dimension
- if (widthOut < heightOut)
- widthOut = heightOut;
-
- switch (mComponents)
- {
- case 1:
- for (int x = 0; x < widthOut; ++x)
- pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+1]) >> 1;
- break;
-
- case 2:
- for (int x = 0; x < widthOut*2; x += 2)
- {
- pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+2]) >> 1;
- pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+3]) >> 1;
- }
- break;
-
- case 3:
- for (int x = 0; x < widthOut*3; x += 3)
- {
- pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+3]) >> 1;
- pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+4]) >> 1;
- pixelDataOut[x+2] = ((unsigned)pixelDataIn[x*2+2] + pixelDataIn[x*2+5]) >> 1;
- }
- break;
-
- case 4:
- for (int x = 0; x < widthOut*4; x += 4)
- {
- pixelDataOut[x] = ((unsigned)pixelDataIn[x*2] + pixelDataIn[x*2+4]) >> 1;
- pixelDataOut[x+1] = ((unsigned)pixelDataIn[x*2+1] + pixelDataIn[x*2+5]) >> 1;
- pixelDataOut[x+2] = ((unsigned)pixelDataIn[x*2+2] + pixelDataIn[x*2+6]) >> 1;
- pixelDataOut[x+3] = ((unsigned)pixelDataIn[x*2+3] + pixelDataIn[x*2+7]) >> 1;
- }
- break;
- }
- }
- // 2D case
- else
- {
- switch (mComponents)
- {
- case 1:
- for (int y = 0; y < heightOut; ++y)
- {
- const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth];
- const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth];
- unsigned char* out = &pixelDataOut[y*widthOut];
-
- for (int x = 0; x < widthOut; ++x)
- {
- out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+1] + inLower[x*2] + inLower[x*2+1]) >> 2;
- }
- }
- break;
-
- case 2:
- for (int y = 0; y < heightOut; ++y)
- {
- const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth*2];
- const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth*2];
- unsigned char* out = &pixelDataOut[y*widthOut*2];
-
- for (int x = 0; x < widthOut*2; x += 2)
- {
- out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+2] + inLower[x*2] + inLower[x*2+2]) >> 2;
- out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+3] + inLower[x*2+1] + inLower[x*2+3]) >> 2;
- }
- }
- break;
-
- case 3:
- for (int y = 0; y < heightOut; ++y)
- {
- const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth*3];
- const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth*3];
- unsigned char* out = &pixelDataOut[y*widthOut*3];
-
- for (int x = 0; x < widthOut*3; x += 3)
- {
- out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+3] + inLower[x*2] + inLower[x*2+3]) >> 2;
- out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+4] + inLower[x*2+1] + inLower[x*2+4]) >> 2;
- out[x+2] = ((unsigned)inUpper[x*2+2] + inUpper[x*2+5] + inLower[x*2+2] + inLower[x*2+5]) >> 2;
- }
- }
- break;
-
- case 4:
- for (int y = 0; y < heightOut; ++y)
- {
- const unsigned char* inUpper = &pixelDataIn[(y*2)*mWidth*4];
- const unsigned char* inLower = &pixelDataIn[(y*2+1)*mWidth*4];
- unsigned char* out = &pixelDataOut[y*widthOut*4];
-
- for (int x = 0; x < widthOut*4; x += 4)
- {
- out[x] = ((unsigned)inUpper[x*2] + inUpper[x*2+4] + inLower[x*2] + inLower[x*2+4]) >> 2;
- out[x+1] = ((unsigned)inUpper[x*2+1] + inUpper[x*2+5] + inLower[x*2+1] + inLower[x*2+5]) >> 2;
- out[x+2] = ((unsigned)inUpper[x*2+2] + inUpper[x*2+6] + inLower[x*2+2] + inLower[x*2+6]) >> 2;
- out[x+3] = ((unsigned)inUpper[x*2+3] + inUpper[x*2+7] + inLower[x*2+3] + inLower[x*2+7]) >> 2;
- }
- }
- break;
- }
- }
-
- return mipImage;
- }
- CompressedLevel Image::getCompressedLevel(unsigned index) const
- {
- CompressedLevel level;
-
- if (mCompressedFormat == CF_NONE)
- SAFE_EXCEPTION_RET("Image is not compressed", level);
- if (index >= mCompressedLevels)
- SAFE_EXCEPTION_RET("Illegal compressed image mip level", level);
-
- level.mWidth = mWidth;
- level.mHeight = mHeight;
- level.mBlockSize = (mCompressedFormat == CF_DXT1) ? 8 : 16;
- unsigned i = 0;
- unsigned offset = 0;
-
- for (;;)
- {
- if (!level.mWidth)
- level.mWidth = 1;
- if (!level.mHeight)
- level.mHeight = 1;
-
- level.mRowSize = ((level.mWidth + 3) / 4) * level.mBlockSize;
- level.mRows = ((level.mHeight + 3) / 4);
- level.mData = mData.getPtr() + offset;
- level.mDataSize = level.mRows * level.mRowSize;
-
- if (offset + level.mDataSize > getMemoryUse())
- SAFE_EXCEPTION_RET("Compressed level is outside image data. Offset: " + toString(offset) + " Size: " + toString(level.mDataSize) + " Datasize: " + toString(getMemoryUse()), level);
-
- if (i == index)
- return level;
-
- offset += level.mDataSize;
- level.mWidth /= 2;
- level.mHeight /= 2;
- ++i;
- }
- }
|