diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index 7e07510b..e813c359 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -87,7 +87,6 @@ FIND_PACKAGE(OpenAL REQUIRED) FIND_PACKAGE(VorbisFile REQUIRED) FIND_PACKAGE(Vorbis REQUIRED) FIND_PACKAGE(Ogg REQUIRED) -FIND_PACKAGE(PNG REQUIRED) IF(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") MESSAGE(STATUS "Debug information: ON") @@ -148,7 +147,6 @@ INCLUDE_DIRECTORIES( ${PHYSFS_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} - ${PNG_INCLUDE_DIR} "${CMAKE_CURRENT_LIST_DIR}/.." ) @@ -161,7 +159,6 @@ SET(framework_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY} ${OPENGL_LIBRARIES} ${OPENAL_LIBRARY} - ${PNG_LIBRARY} ${ADDITIONAL_LIBRARIES} ) diff --git a/src/framework/cmake/FindPNG.cmake b/src/framework/cmake/FindPNG.cmake index bc3555d1..5e5650df 100644 --- a/src/framework/cmake/FindPNG.cmake +++ b/src/framework/cmake/FindPNG.cmake @@ -3,7 +3,7 @@ # PNG_INCLUDE_DIR - the PNG include directory # PNG_LIBRARY - the PNG library -FIND_PATH(PNG_INCLUDE_DIR NAMES png.h) +FIND_PATH(PNG_INCLUDE_DIR NAMES png.h PATH_SUFFIXES libpng15 libpng) FIND_LIBRARY(PNG_LIBRARY NAMES libpng15.a libpng.a png15 png) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(PNG DEFAULT_MSG PNG_LIBRARY PNG_INCLUDE_DIR) diff --git a/src/framework/core/resourcemanager.cpp b/src/framework/core/resourcemanager.cpp index 1c72613e..347fab07 100644 --- a/src/framework/core/resourcemanager.cpp +++ b/src/framework/core/resourcemanager.cpp @@ -1,217 +1,223 @@ -/* - * Copyright (c) 2010-2012 OTClient - * - * 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 "resourcemanager.h" -#include "filestream.h" - -#include -#include - -#include - -ResourceManager g_resources; - -void ResourceManager::init(const char *argv0) -{ - PHYSFS_init(argv0); -} - -void ResourceManager::terminate() -{ - PHYSFS_deinit(); -} - -bool ResourceManager::setupWriteDir(const std::string& appWriteDirName) -{ - std::string userDir = PHYSFS_getUserDir(); - std::string dirName = Fw::mkstr(".", appWriteDirName); - std::string writeDir = userDir + dirName; - if(!PHYSFS_setWriteDir(writeDir.c_str())) { - if(!PHYSFS_setWriteDir(userDir.c_str())) - return false; - if(!PHYSFS_mkdir(dirName.c_str())) { - PHYSFS_setWriteDir(NULL); - return false; - } - if(!PHYSFS_setWriteDir(writeDir.c_str())) - return false; - } - addToSearchPath(writeDir); - return true; -} - -bool ResourceManager::addToSearchPath(const std::string& path, bool insertInFront) -{ - if(!PHYSFS_addToSearchPath(path.c_str(), insertInFront ? 0 : 1)) - return false; - return true; -} - -void ResourceManager::searchAndAddPackages(const std::string& packagesDir, const std::string& packageExt, bool append) -{ - auto files = listDirectoryFiles(resolvePath(packagesDir)); - for(const std::string& file : files) { - if(boost::ends_with(file, packageExt)) - addToSearchPath(packagesDir + "/" + file, !append); - } -} - -bool ResourceManager::fileExists(const std::string& fileName) -{ - return (PHYSFS_exists(resolvePath(fileName).c_str()) && !PHYSFS_isDirectory(resolvePath(fileName).c_str())); -} - -bool ResourceManager::directoryExists(const std::string& directoryName) -{ - return (PHYSFS_isDirectory(resolvePath(directoryName).c_str())); -} - -void ResourceManager::loadFile(const std::string& fileName, std::iostream& out) -{ - std::string fullPath = resolvePath(fileName); - out.clear(std::ios::goodbit); - PHYSFS_file* file = PHYSFS_openRead(fullPath.c_str()); - if(!file) { - out.clear(std::ios::failbit); - Fw::throwException("failed to load file '", fullPath.c_str(), "': ", PHYSFS_getLastError()); - } else { - int fileSize = PHYSFS_fileLength(file); - if(fileSize > 0) { - std::vector buffer(fileSize); - PHYSFS_read(file, (void*)&buffer[0], 1, fileSize); - out.write(&buffer[0], fileSize); - } else - out.clear(std::ios::eofbit); - PHYSFS_close(file); - out.seekg(0, std::ios::beg); - } -} - -std::string ResourceManager::loadFile(const std::string& fileName) -{ - std::stringstream fin; - loadFile(fileName, fin); - return fin.str(); -} - -bool ResourceManager::saveFile(const std::string& fileName, const uchar* data, uint size) -{ - PHYSFS_file* file = PHYSFS_openWrite(fileName.c_str()); +/* + * Copyright (c) 2010-2012 OTClient + * + * 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 "resourcemanager.h" +#include "filestream.h" + +#include +#include + +#include + +ResourceManager g_resources; + +void ResourceManager::init(const char *argv0) +{ + PHYSFS_init(argv0); +} + +void ResourceManager::terminate() +{ + PHYSFS_deinit(); +} + +bool ResourceManager::setupWriteDir(const std::string& appWriteDirName) +{ + std::string userDir = PHYSFS_getUserDir(); + std::string dirName = Fw::mkstr(".", appWriteDirName); + std::string writeDir = userDir + dirName; + if(!PHYSFS_setWriteDir(writeDir.c_str())) { + if(!PHYSFS_setWriteDir(userDir.c_str())) + return false; + if(!PHYSFS_mkdir(dirName.c_str())) { + PHYSFS_setWriteDir(NULL); + return false; + } + if(!PHYSFS_setWriteDir(writeDir.c_str())) + return false; + } + addToSearchPath(writeDir); + return true; +} + +bool ResourceManager::addToSearchPath(const std::string& path, bool insertInFront) +{ + if(!PHYSFS_addToSearchPath(path.c_str(), insertInFront ? 0 : 1)) + return false; + return true; +} + +void ResourceManager::searchAndAddPackages(const std::string& packagesDir, const std::string& packageExt, bool append) +{ + auto files = listDirectoryFiles(resolvePath(packagesDir)); + for(const std::string& file : files) { + if(boost::ends_with(file, packageExt)) + addToSearchPath(packagesDir + "/" + file, !append); + } +} + +bool ResourceManager::fileExists(const std::string& fileName) +{ + return (PHYSFS_exists(resolvePath(fileName).c_str()) && !PHYSFS_isDirectory(resolvePath(fileName).c_str())); +} + +bool ResourceManager::directoryExists(const std::string& directoryName) +{ + return (PHYSFS_isDirectory(resolvePath(directoryName).c_str())); +} + +void ResourceManager::loadFile(const std::string& fileName, std::iostream& out) +{ + std::string fullPath = resolvePath(fileName); + out.clear(std::ios::goodbit); + PHYSFS_file* file = PHYSFS_openRead(fullPath.c_str()); + if(!file) { + out.clear(std::ios::failbit); + Fw::throwException("failed to load file '", fullPath.c_str(), "': ", PHYSFS_getLastError()); + } else { + int fileSize = PHYSFS_fileLength(file); + if(fileSize > 0) { + std::vector buffer(fileSize); + PHYSFS_read(file, (void*)&buffer[0], 1, fileSize); + out.write(&buffer[0], fileSize); + } else + out.clear(std::ios::eofbit); + PHYSFS_close(file); + out.seekg(0, std::ios::beg); + } +} + +std::string ResourceManager::loadFile(const std::string& fileName) +{ + std::stringstream fin; + loadFile(fileName, fin); + return fin.str(); +} + +bool ResourceManager::saveFile(const std::string& fileName, const uchar* data, uint size) +{ + PHYSFS_file* file = PHYSFS_openWrite(fileName.c_str()); if(!file) { logError(PHYSFS_getLastError()); return false; - } - - PHYSFS_write(file, (void*)data, size, 1); - PHYSFS_close(file); - return true; -} - -bool ResourceManager::saveFile(const std::string& fileName, std::iostream& in) -{ - std::streampos oldPos = in.tellg(); - in.seekg(0, std::ios::end); - std::streampos size = in.tellg(); - in.seekg(0, std::ios::beg); - std::vector buffer(size); - in.read(&buffer[0], size); - bool ret = saveFile(fileName, (const uchar*)&buffer[0], size); - in.seekg(oldPos, std::ios::beg); - return ret; -} - -bool ResourceManager::saveFile(const std::string& fileName, const std::string& data) -{ - return saveFile(fileName, (const uchar*)data.c_str(), data.size()); -} - -FileStreamPtr ResourceManager::openFile(const std::string& fileName) -{ - std::string fullPath = resolvePath(fileName); - PHYSFS_File* file = PHYSFS_openRead(fullPath.c_str()); - if(!file) { - logTraceError("unable to open file '", fullPath, "': ", PHYSFS_getLastError()); - return nullptr; - } - return FileStreamPtr(new FileStream(fullPath, file)); -} - -FileStreamPtr ResourceManager::appendFile(const std::string& fileName) -{ - PHYSFS_File* file = PHYSFS_openAppend(fileName.c_str()); - if(!file) { - logTraceError("failed to append file '", fileName, "': ", PHYSFS_getLastError()); - return nullptr; - } - return FileStreamPtr(new FileStream(fileName, file)); -} - -FileStreamPtr ResourceManager::createFile(const std::string& fileName) -{ - PHYSFS_File* file = PHYSFS_openWrite(fileName.c_str()); - if(!file) { - logTraceError("failed to create file '", fileName, "': ", PHYSFS_getLastError()); - return nullptr; - } - return FileStreamPtr(new FileStream(fileName, file)); -} - -bool ResourceManager::deleteFile(const std::string& fileName) -{ - return PHYSFS_delete(resolvePath(fileName).c_str()) != 0; -} - -std::list ResourceManager::listDirectoryFiles(const std::string& directoryPath) -{ - std::list files; - auto rc = PHYSFS_enumerateFiles(resolvePath(directoryPath).c_str()); - - for(int i = 0; rc[i] != NULL; i++) - files.push_back(rc[i]); - - PHYSFS_freeList(rc); - return files; -} - -std::string ResourceManager::resolvePath(const std::string& path) -{ - std::string fullPath; - if(boost::starts_with(path, "/")) - fullPath = path; - else { - std::string scriptPath = "/" + g_lua.getCurrentSourcePath(); - if(!scriptPath.empty()) - fullPath += scriptPath + "/"; - fullPath += path; - } - if(!(boost::starts_with(fullPath, "/"))) - logTraceWarning("the following file path is not fully resolved: ", path); - boost::replace_all(fullPath, "//", "/"); - return fullPath; -} - -std::string ResourceManager::getBaseDir() -{ - return PHYSFS_getBaseDir(); -} - + } + + PHYSFS_write(file, (void*)data, size, 1); + PHYSFS_close(file); + return true; +} + +bool ResourceManager::saveFile(const std::string& fileName, std::iostream& in) +{ + std::streampos oldPos = in.tellg(); + in.seekg(0, std::ios::end); + std::streampos size = in.tellg(); + in.seekg(0, std::ios::beg); + std::vector buffer(size); + in.read(&buffer[0], size); + bool ret = saveFile(fileName, (const uchar*)&buffer[0], size); + in.seekg(oldPos, std::ios::beg); + return ret; +} + +bool ResourceManager::saveFile(const std::string& fileName, const std::string& data) +{ + return saveFile(fileName, (const uchar*)data.c_str(), data.size()); +} + +FileStreamPtr ResourceManager::openFile(const std::string& fileName) +{ + std::string fullPath = resolvePath(fileName); + PHYSFS_File* file = PHYSFS_openRead(fullPath.c_str()); + if(!file) { + logTraceError("unable to open file '", fullPath, "': ", PHYSFS_getLastError()); + return nullptr; + } + return FileStreamPtr(new FileStream(fullPath, file)); +} + +FileStreamPtr ResourceManager::appendFile(const std::string& fileName) +{ + PHYSFS_File* file = PHYSFS_openAppend(fileName.c_str()); + if(!file) { + logTraceError("failed to append file '", fileName, "': ", PHYSFS_getLastError()); + return nullptr; + } + return FileStreamPtr(new FileStream(fileName, file)); +} + +FileStreamPtr ResourceManager::createFile(const std::string& fileName) +{ + PHYSFS_File* file = PHYSFS_openWrite(fileName.c_str()); + if(!file) { + logTraceError("failed to create file '", fileName, "': ", PHYSFS_getLastError()); + return nullptr; + } + return FileStreamPtr(new FileStream(fileName, file)); +} + +bool ResourceManager::deleteFile(const std::string& fileName) +{ + return PHYSFS_delete(resolvePath(fileName).c_str()) != 0; +} + + +bool ResourceManager::makeDir(const std::string directory) +{ + return PHYSFS_mkdir(directory.c_str()); +} + +std::list ResourceManager::listDirectoryFiles(const std::string& directoryPath) +{ + std::list files; + auto rc = PHYSFS_enumerateFiles(resolvePath(directoryPath).c_str()); + + for(int i = 0; rc[i] != NULL; i++) + files.push_back(rc[i]); + + PHYSFS_freeList(rc); + return files; +} + +std::string ResourceManager::resolvePath(const std::string& path) +{ + std::string fullPath; + if(boost::starts_with(path, "/")) + fullPath = path; + else { + std::string scriptPath = "/" + g_lua.getCurrentSourcePath(); + if(!scriptPath.empty()) + fullPath += scriptPath + "/"; + fullPath += path; + } + if(!(boost::starts_with(fullPath, "/"))) + logTraceWarning("the following file path is not fully resolved: ", path); + boost::replace_all(fullPath, "//", "/"); + return fullPath; +} + +std::string ResourceManager::getBaseDir() +{ + return PHYSFS_getBaseDir(); +} + diff --git a/src/framework/core/resourcemanager.h b/src/framework/core/resourcemanager.h index dea54612..a24691a7 100644 --- a/src/framework/core/resourcemanager.h +++ b/src/framework/core/resourcemanager.h @@ -50,6 +50,7 @@ public: FileStreamPtr createFile(const std::string& fileName); bool deleteFile(const std::string& fileName); + bool makeDir(const std::string directory); std::list listDirectoryFiles(const std::string& directoryPath = ""); std::string resolvePath(const std::string& path); diff --git a/src/framework/thirdparty/apngloader.cpp b/src/framework/thirdparty/apngloader.cpp index cc25de66..fe6ec379 100644 --- a/src/framework/thirdparty/apngloader.cpp +++ b/src/framework/thirdparty/apngloader.cpp @@ -557,7 +557,7 @@ int load_apng(std::stringstream& file, struct apng_data *apng) unsigned char * pImg2; unsigned char * pDst1; unsigned char * pDst2; - unsigned int * frames_delay; + unsigned short* frames_delay; file.read((char*)sig, 8); if(!file.eof() && memcmp(sig, png_sign, 8) == 0) { @@ -677,7 +677,7 @@ int load_apng(std::stringstream& file, struct apng_data *apng) frames = read32(file); if(frames_delay) free(frames_delay); - frames_delay = (unsigned int*)malloc(frames*sizeof(int)); + frames_delay = (unsigned short*)malloc(frames*sizeof(int)); loops = read32(file); crc = read32(file); if (pOut1) @@ -862,6 +862,279 @@ int load_apng(std::stringstream& file, struct apng_data *apng) return 0; } +void write_chunk(std::ostream& f, const char* name, unsigned char* data, unsigned int length) +{ + unsigned int crc = crc32(0, Z_NULL, 0); + unsigned int len = swap32(length); + + f.write((char*)&len, 4); + f.write(name, 4); + crc = crc32(crc, (const Bytef*)name, 4); + + if(data != NULL && length > 0) { + f.write((char*)data, length); + crc = crc32(crc, data, length); + } + + crc = swap32(crc); + f.write((char*)&crc, 4); +} + +void write_IDATs(std::ostream& f, unsigned char* data, unsigned int length, unsigned int idat_size) +{ + unsigned int z_cmf = data[0]; + + if((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) { + if(length >= 2) { + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + + while(idat_size <= half_z_window_size && half_z_window_size >= 256) { + z_cinfo--; + half_z_window_size >>= 1; + } + + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + + if(data[0] != (unsigned char)z_cmf) { + data[0] = (unsigned char)z_cmf; + data[1] &= 0xe0; + data[1] += (unsigned char)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + + while(length > 0) { + unsigned int ds = length; + + if(ds > PNG_ZBUF_SIZE) + ds = PNG_ZBUF_SIZE; + + write_chunk(f, "IDAT", data, ds); + + data += ds; + length -= ds; + } +} + +void save_png(std::stringstream& f, int width, int height, int channels, unsigned char *pixels) +{ + unsigned int bpp = 4; + unsigned char coltype = 0; + + if(channels == 3) + coltype = 2; + else if (channels == 2) + coltype = 4; + else if (channels == 4) + coltype = 6; + + struct IHDR { + unsigned int mWidth; + unsigned int mHeight; + unsigned char mDepth; + unsigned char mColorType; + unsigned char mCompression; + unsigned char mFilterMethod; + unsigned char mInterlaceMethod; + } ihdr = { swap32(width), swap32(height), 8, coltype, 0, 0, 0 }; + + z_stream zstream1; + z_stream zstream2; + unsigned int i, j; + + unsigned int rowbytes = width * bpp; + unsigned int idat_size = (rowbytes + 1) * height; + unsigned int zbuf_size = idat_size + ((idat_size + 7) >> 3) + ((idat_size + 63) >> 6) + 11; + + unsigned char* row_buf = (unsigned char*)malloc(rowbytes + 1); + unsigned char* sub_row = (unsigned char*)malloc(rowbytes + 1); + unsigned char* up_row = (unsigned char*)malloc(rowbytes + 1); + unsigned char* avg_row = (unsigned char*)malloc(rowbytes + 1); + unsigned char* paeth_row = (unsigned char*)malloc(rowbytes + 1); + unsigned char* zbuf1 = (unsigned char*)malloc(zbuf_size); + unsigned char* zbuf2 = (unsigned char*)malloc(zbuf_size); + + if(!row_buf || !sub_row || !up_row || !avg_row || !paeth_row || !zbuf1 || !zbuf2) + return; + + row_buf[0] = 0; + sub_row[0] = 1; + up_row[0] = 2; + avg_row[0] = 3; + paeth_row[0] = 4; + + zstream1.data_type = Z_BINARY; + zstream1.zalloc = Z_NULL; + zstream1.zfree = Z_NULL; + zstream1.opaque = Z_NULL; + deflateInit2(&zstream1, Z_BEST_COMPRESSION, 8, 15, 8, Z_DEFAULT_STRATEGY); + + zstream2.data_type = Z_BINARY; + zstream2.zalloc = Z_NULL; + zstream2.zfree = Z_NULL; + zstream2.opaque = Z_NULL; + deflateInit2(&zstream2, Z_BEST_COMPRESSION, 8, 15, 8, Z_FILTERED); + + int a, b, c, pa, pb, pc, p, v; + unsigned char* prev; + unsigned char* row; + + f.write((char*)png_sign, 8); + write_chunk(f, "IHDR", (unsigned char*)(&ihdr), 13); + + if(palsize > 0) + write_chunk(f, "PLTE", (unsigned char*)(&pal), palsize * 3); + + if(trnssize > 0) + write_chunk(f, "tRNS", trns, trnssize); + + zstream1.next_out = zbuf1; + zstream1.avail_out = zbuf_size; + zstream2.next_out = zbuf2; + zstream2.avail_out = zbuf_size; + + prev = NULL; + row = pixels; + + for(j = 0; j < (unsigned int)height; j++) { + unsigned char* out; + unsigned int sum = 0; + unsigned char* best_row = row_buf; + unsigned int mins = ((unsigned int)(-1)) >> 1; + + out = row_buf + 1; + + for(i = 0; i < rowbytes; i++) { + v = out[i] = row[i]; + sum += (v < 128) ? v : 256 - v; + } + + mins = sum; + + sum = 0; + out = sub_row + 1; + + for(i = 0; i < bpp; i++) { + v = out[i] = row[i]; + sum += (v < 128) ? v : 256 - v; + } + + for(i = bpp; i < rowbytes; i++) { + v = out[i] = row[i] - row[i - bpp]; + sum += (v < 128) ? v : 256 - v; + + if(sum > mins) break; + } + + if(sum < mins) { + mins = sum; + best_row = sub_row; + } + + if(prev) { + sum = 0; + out = up_row + 1; + + for(i = 0; i < rowbytes; i++) { + v = out[i] = row[i] - prev[i]; + sum += (v < 128) ? v : 256 - v; + + if(sum > mins) break; + } + + if(sum < mins) { + mins = sum; + best_row = up_row; + } + + sum = 0; + out = avg_row + 1; + + for(i = 0; i < bpp; i++) { + v = out[i] = row[i] - prev[i] / 2; + sum += (v < 128) ? v : 256 - v; + } + + for(i = bpp; i < rowbytes; i++) { + v = out[i] = row[i] - (prev[i] + row[i - bpp]) / 2; + sum += (v < 128) ? v : 256 - v; + + if(sum > mins) break; + } + + if(sum < mins) { + mins = sum; + best_row = avg_row; + } + + sum = 0; + out = paeth_row + 1; + + for(i = 0; i < bpp; i++) { + v = out[i] = row[i] - prev[i]; + sum += (v < 128) ? v : 256 - v; + } + + for(i = bpp; i < rowbytes; i++) { + a = row[i - bpp]; + b = prev[i]; + c = prev[i - bpp]; + p = b - c; + pc = a - c; + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); + p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; + v = out[i] = row[i] - p; + sum += (v < 128) ? v : 256 - v; + + if(sum > mins) break; + } + + if(sum < mins) { + best_row = paeth_row; + } + } + + zstream1.next_in = row_buf; + zstream1.avail_in = rowbytes + 1; + deflate(&zstream1, Z_NO_FLUSH); + + zstream2.next_in = best_row; + zstream2.avail_in = rowbytes + 1; + deflate(&zstream2, Z_NO_FLUSH); + + prev = row; + row += rowbytes; + } + + deflate(&zstream1, Z_FINISH); + deflate(&zstream2, Z_FINISH); + + if(zstream1.total_out <= zstream2.total_out) + write_IDATs(f, zbuf1, zstream1.total_out, idat_size); + else + write_IDATs(f, zbuf2, zstream2.total_out, idat_size); + + deflateReset(&zstream1); + zstream1.data_type = Z_BINARY; + deflateReset(&zstream2); + zstream2.data_type = Z_BINARY; + + write_chunk(f, "IEND", 0, 0); + + deflateEnd(&zstream1); + deflateEnd(&zstream2); + free(zbuf1); + free(zbuf2); + free(row_buf); + free(sub_row); + free(up_row); + free(avg_row); + free(paeth_row); +} + void free_apng(struct apng_data *apng) { if(apng->pdata) diff --git a/src/framework/thirdparty/apngloader.h b/src/framework/thirdparty/apngloader.h index cb6cfc33..92257823 100644 --- a/src/framework/thirdparty/apngloader.h +++ b/src/framework/thirdparty/apngloader.h @@ -35,11 +35,12 @@ struct apng_data { unsigned char coltype; unsigned int num_frames; unsigned int num_plays; - unsigned int *frames_delay; // each frame delay in ms + unsigned short *frames_delay; // each frame delay in ms }; // returns -1 on error, 0 on success int load_apng(std::stringstream& file, struct apng_data *apng); +void save_png(std::stringstream& file, int width, int height, int channels, unsigned char *pixels); void free_apng(struct apng_data *apng); #endif diff --git a/src/otclient/core/spritemanager.cpp b/src/otclient/core/spritemanager.cpp index 86f7ad0c..3911e1b3 100644 --- a/src/otclient/core/spritemanager.cpp +++ b/src/otclient/core/spritemanager.cpp @@ -1,282 +1,238 @@ -/* - * Copyright (c) 2010-2012 OTClient - * - * 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 "spritemanager.h" -#include -#include -#include +/* + * Copyright (c) 2010-2012 OTClient + * + * 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 "spritemanager.h" +#include +#include +#include #include -#include - -SpriteManager g_sprites; - -SpriteManager::SpriteManager() -{ - m_spritesCount = 0; - m_signature = 0; -} - -bool SpriteManager::load(const std::string& file) -{ - try { - m_spritesFile = g_resources.openFile(file); - if(!m_spritesFile) - return false; - - // cache file buffer to avoid lags from hard drive - m_spritesFile->cache(); - - m_signature = m_spritesFile->getU32(); - m_spritesCount = m_spritesFile->getU16(); - m_sprites.resize(m_spritesCount); - m_loaded = true; - return true; - } catch(Exception& e) { - logError("Failed to load sprites from '", file, "': ", e.what()); - return false; - } -} - -void SpriteManager::unload() -{ - m_sprites.clear(); - m_spritesCount = 0; - m_signature = 0; -} - -void SpriteManager::preloadSprites() -{ - // preload every 50 sprites, periodically - const int burst = 50; - const int interval = 10; - auto preload = [this](int start) { - for(int i=start;iseek(((id-1) * 4) + 6); - - uint32 spriteAddress = m_spritesFile->getU32(); - - // no sprite? return an empty texture - if(spriteAddress == 0) - return g_graphics.getEmptyTexture(); - - m_spritesFile->seek(spriteAddress); - - // skip color key - m_spritesFile->getU8(); - m_spritesFile->getU8(); - m_spritesFile->getU8(); - - uint16 pixelDataSize = m_spritesFile->getU16(); - - static std::vector pixels(SPRITE_SIZE); - int writePos = 0; - int read = 0; - - // decompress pixels - while(read < pixelDataSize) { - uint16 transparentPixels = m_spritesFile->getU16(); - uint16 coloredPixels = m_spritesFile->getU16(); - - if(writePos + transparentPixels*4 + coloredPixels*3 >= SPRITE_SIZE) - return g_graphics.getEmptyTexture(); - - for(int i = 0; i < transparentPixels; i++) { - pixels[writePos + 0] = 0x00; - pixels[writePos + 1] = 0x00; - pixels[writePos + 2] = 0x00; - pixels[writePos + 3] = 0x00; - writePos += 4; - } - - for(int i = 0; i < coloredPixels; i++) { - pixels[writePos + 0] = m_spritesFile->getU8(); - pixels[writePos + 1] = m_spritesFile->getU8(); - pixels[writePos + 2] = m_spritesFile->getU8(); - pixels[writePos + 3] = 0xFF; - - writePos += 4; - } - - read += 4 + (3 * coloredPixels); - } - - // fill remaining pixels with alpha - while(writePos < SPRITE_SIZE) { - pixels[writePos + 0] = 0x00; - pixels[writePos + 1] = 0x00; - pixels[writePos + 2] = 0x00; - pixels[writePos + 3] = 0x00; - writePos += 4; - } - - TexturePtr spriteTex(new Texture(32, 32, 4, &pixels[0])); - spriteTex->setSmooth(true); - - if(g_graphics.canUseMipmaps()) - spriteTex->generateSoftwareMipmaps(pixels); - - return spriteTex; -} - -TexturePtr& SpriteManager::getSpriteTexture(int id) -{ - if(id == 0) - return g_graphics.getEmptyTexture(); - - assert(id > 0 && id <= m_spritesCount); - - // load sprites on demand - TexturePtr& sprite = m_sprites[id-1]; - if(!sprite) - sprite = loadSpriteTexture(id); - - //TODO: release unused sprites textures after X seconds - // to avoid massive texture allocations - return sprite; +#include +#include + +SpriteManager g_sprites; + +SpriteManager::SpriteManager() +{ + m_spritesCount = 0; + m_signature = 0; } -bool SpriteManager::exportSprites() +bool SpriteManager::load(const std::string& file) { + try { + m_spritesFile = g_resources.openFile(file); + if(!m_spritesFile) + return false; - for(volatile int i = 1; i <= m_spritesCount; i++) { - m_spritesFile->seek(((i-1) * 4) + 6); - - uint32 spriteAddress = m_spritesFile->getU32(); - if(spriteAddress == 0) - continue; - - m_spritesFile->seek(spriteAddress); - - // skip color key - m_spritesFile->getU8(); - m_spritesFile->getU8(); - m_spritesFile->getU8(); - - uint16 pixelDataSize = m_spritesFile->getU16(); - - uchar pixels[SPRITE_SIZE]; - int writePos = 0; - int read = 0; - - // decompress pixels - while(read < pixelDataSize) { - uint16 transparentPixels = m_spritesFile->getU16(); - uint16 coloredPixels = m_spritesFile->getU16(); - - if(writePos + transparentPixels*4 + coloredPixels*3 >= SPRITE_SIZE) - return false; // skip the whole process or just ignore this sprite? - - for(int i = 0; i < transparentPixels; i++) { - pixels[writePos + 0] = 0x00; - pixels[writePos + 1] = 0x00; - pixels[writePos + 2] = 0x00; - pixels[writePos + 3] = 0x00; - writePos += 4; - } - - for(int i = 0; i < coloredPixels; i++) { - pixels[writePos + 0] = m_spritesFile->getU8(); - pixels[writePos + 1] = m_spritesFile->getU8(); - pixels[writePos + 2] = m_spritesFile->getU8(); - pixels[writePos + 3] = 0xFF; - - writePos += 4; - } - - read += 4 + (3 * coloredPixels); - } - - // fill remaining pixels with alpha - while(writePos < SPRITE_SIZE) { - pixels[writePos + 0] = 0x00; - pixels[writePos + 1] = 0x00; - pixels[writePos + 2] = 0x00; - pixels[writePos + 3] = 0x00; - writePos += 4; + // cache file buffer to avoid lags from hard drive + m_spritesFile->cache(); + + m_signature = m_spritesFile->getU32(); + m_spritesCount = m_spritesFile->getU16(); + m_sprites.resize(m_spritesCount); + m_loaded = true; + return true; + } catch(Exception& e) { + logError("Failed to load sprites from '", file, "': ", e.what()); + return false; + } +} + +void SpriteManager::unload() +{ + m_sprites.clear(); + m_spritesCount = 0; + m_signature = 0; +} + +void SpriteManager::preloadSprites() +{ + // preload every 50 sprites, periodically + const int burst = 50; + const int interval = 10; + auto preload = [this](int start) { + for(int i=start;iseek(((id-1) * 4) + 6); - png_structp pPng = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if(!pPng) { - fclose(pFile); - return false; - } + uint32 spriteAddress = m_spritesFile->getU32(); - png_infop pPngInfo = png_create_info_struct(pPng); - if(!pPngInfo) { - fclose(pFile); - png_destroy_write_struct(&pPng, NULL); - return false; + // no sprite? return an empty texture + if(spriteAddress == 0) + return g_graphics.getEmptyTexture(); + + m_spritesFile->seek(spriteAddress); + + // skip color key + m_spritesFile->getU8(); + m_spritesFile->getU8(); + m_spritesFile->getU8(); + + uint16 pixelDataSize = m_spritesFile->getU16(); + + static std::vector pixels(SPRITE_SIZE); + int writePos = 0; + int read = 0; + + // decompress pixels + while(read < pixelDataSize) { + uint16 transparentPixels = m_spritesFile->getU16(); + uint16 coloredPixels = m_spritesFile->getU16(); + + if(writePos + transparentPixels*4 + coloredPixels*3 >= SPRITE_SIZE) + return g_graphics.getEmptyTexture(); + + for(int i = 0; i < transparentPixels; i++) { + pixels[writePos + 0] = 0x00; + pixels[writePos + 1] = 0x00; + pixels[writePos + 2] = 0x00; + pixels[writePos + 3] = 0x00; + writePos += 4; } - if(setjmp(png_jmpbuf(pPng))) { - fclose(pFile); - png_destroy_write_struct(&pPng, &pPngInfo); - return false; + for(int i = 0; i < coloredPixels; i++) { + pixels[writePos + 0] = m_spritesFile->getU8(); + pixels[writePos + 1] = m_spritesFile->getU8(); + pixels[writePos + 2] = m_spritesFile->getU8(); + pixels[writePos + 3] = 0xFF; + + writePos += 4; } - png_init_io(pPng, pFile); - png_set_compression_level(pPng, PNG_COMPRESSION); + read += 4 + (3 * coloredPixels); + } + + // fill remaining pixels with alpha + while(writePos < SPRITE_SIZE) { + pixels[writePos + 0] = 0x00; + pixels[writePos + 1] = 0x00; + pixels[writePos + 2] = 0x00; + pixels[writePos + 3] = 0x00; + writePos += 4; + } + + TexturePtr spriteTex(new Texture(32, 32, 4, &pixels[0])); + spriteTex->setSmooth(true); + + if(g_graphics.canUseMipmaps()) + spriteTex->generateSoftwareMipmaps(pixels); + + return spriteTex; +} - int bitDepthPerChannel = SPRITE_CHANNELS/4*8; - int colorType = PNG_COLOR_TYPE_RGB_ALPHA; +TexturePtr& SpriteManager::getSpriteTexture(int id) +{ + if(id == 0) + return g_graphics.getEmptyTexture(); - png_set_IHDR(pPng, pPngInfo, SPRITE_WIDTH, SPRITE_HEIGHT, bitDepthPerChannel, colorType, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + assert(id > 0 && id <= m_spritesCount); - png_write_info(pPng, pPngInfo); + // load sprites on demand + TexturePtr& sprite = m_sprites[id-1]; + if(!sprite) + sprite = loadSpriteTexture(id); - if(bitDepthPerChannel == 16) // Reverse endian order (PNG is Big Endian) - png_set_swap(pPng); + //TODO: release unused sprites textures after X seconds + // to avoid massive texture allocations + return sprite; +} - int bytesPerRow = SPRITE_WIDTH*SPRITE_CHANNELS; - uint8* pImgData = pixels; +bool SpriteManager::exportSprites() +{ + g_resources.makeDir("sprites"); + std::stringstream ss; + + for(volatile int i = 1; i <= m_spritesCount; i++) { + m_spritesFile->seek(((i-1) * 4) + 6); - for(int row=0; row < SPRITE_HEIGHT; row++) { // Write non-interlaced buffer - png_write_row(pPng, pImgData); - pImgData += bytesPerRow; + uint32 spriteAddress = m_spritesFile->getU32(); + if(spriteAddress == 0) + continue; + + m_spritesFile->seek(spriteAddress); + + // skip color key + m_spritesFile->getU8(); + m_spritesFile->getU8(); + m_spritesFile->getU8(); + + uint16 pixelDataSize = m_spritesFile->getU16(); + + uchar pixels[SPRITE_SIZE]; + int writePos = 0; + int read = 0; + + // decompress pixels + while(read < pixelDataSize) { + uint16 transparentPixels = m_spritesFile->getU16(); + uint16 coloredPixels = m_spritesFile->getU16(); + + if(writePos + transparentPixels*4 + coloredPixels*3 >= SPRITE_SIZE) + return false; // skip the whole process or just ignore this sprite? + + for(int i = 0; i < transparentPixels; i++) { + pixels[writePos + 0] = 0x00; + pixels[writePos + 1] = 0x00; + pixels[writePos + 2] = 0x00; + pixels[writePos + 3] = 0x00; + writePos += 4; + } + + for(int i = 0; i < coloredPixels; i++) { + pixels[writePos + 0] = m_spritesFile->getU8(); + pixels[writePos + 1] = m_spritesFile->getU8(); + pixels[writePos + 2] = m_spritesFile->getU8(); + pixels[writePos + 3] = 0xFF; + + writePos += 4; + } + + read += 4 + (3 * coloredPixels); } - png_write_end(pPng, NULL); - png_destroy_write_struct(&pPng, &pPngInfo); + // fill remaining pixels with alpha + while(writePos < SPRITE_SIZE) { + pixels[writePos + 0] = 0x00; + pixels[writePos + 1] = 0x00; + pixels[writePos + 2] = 0x00; + pixels[writePos + 3] = 0x00; + writePos += 4; + } - fclose(pFile); + // We should get the OTClient and export to that folder... + std::string fileName = Fw::formatString("sprites/%d.png", i); + ss.str(""); + save_png(ss, SPRITE_WIDTH, SPRITE_HEIGHT, SPRITE_CHANNELS, pixels); + g_resources.saveFile(fileName, ss); } return true; -} +} diff --git a/src/otclient/core/spritemanager.h b/src/otclient/core/spritemanager.h index 18e117ce..9c8885f2 100644 --- a/src/otclient/core/spritemanager.h +++ b/src/otclient/core/spritemanager.h @@ -1,74 +1,67 @@ -/* - * Copyright (c) 2010-2012 OTClient - * - * 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. - */ - -#ifndef SPRITEMANAGER_H -#define SPRITEMANAGER_H - -#include +/* + * Copyright (c) 2010-2012 OTClient + * + * 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. + */ + +#ifndef SPRITEMANAGER_H +#define SPRITEMANAGER_H + +#include #include #include -#define SPRITE_WIDTH 32 -#define SPRITE_HEIGHT 32 -#define SPRITE_CHANNELS 4 -#define SPRITE_SIZE SPRITE_WIDTH*SPRITE_HEIGHT*SPRITE_CHANNELS +class SpriteManager +{ + enum { + SPRITE_WIDTH=32, + SPRITE_HEIGHT=32, + SPRITE_CHANNELS=4, + SPRITE_SIZE=SPRITE_WIDTH*SPRITE_HEIGHT*SPRITE_CHANNELS + }; -/* - Compression levels: - None = 0 - Fastest = 1 - Best = 9 -*/ +public: + SpriteManager(); + + bool load(const std::string& file); + void unload(); + void preloadSprites(); -#define PNG_COMPRESSION 6 - -class SpriteManager -{ -public: - SpriteManager(); - - bool load(const std::string& file); - void unload(); - void preloadSprites(); - - uint32 getSignature() { return m_signature; } + uint32 getSignature() { return m_signature; } int getSpritesCount() { return m_spritesCount; } - bool exportSprites(); - - TexturePtr& getSpriteTexture(int id); - bool isLoaded() { return m_loaded; } - -private: - TexturePtr loadSpriteTexture(int id); - - Boolean m_loaded; - uint32 m_signature; - int m_spritesCount; - std::vector m_sprites; - TexturePtr m_transparentSprite; - FileStreamPtr m_spritesFile; -}; - -extern SpriteManager g_sprites; - -#endif + bool exportSprites(); + + TexturePtr& getSpriteTexture(int id); + bool isLoaded() { return m_loaded; } + +private: + TexturePtr loadSpriteTexture(int id); + + Boolean m_loaded; + uint32 m_signature; + int m_spritesCount; + std::vector m_sprites; + TexturePtr m_transparentSprite; + FileStreamPtr m_spritesFile; +}; + +extern SpriteManager g_sprites; + +#endif