diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e21b73f01..8c1865f8b0 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1001,8 +1001,8 @@ set(GRAPHICS_SOURCES src/graphics/font/FontCache.cpp src/graphics/image/Image.cpp src/graphics/image/ImageColorKey.cpp + src/graphics/image/ImageSave.cpp src/graphics/image/stb_image.cpp - src/graphics/image/stb_image_write.cpp src/graphics/particle/Particle.cpp src/graphics/particle/ParticleEffects.cpp src/graphics/particle/ParticleManager.cpp diff --git a/src/graphics/image/Image.cpp b/src/graphics/image/Image.cpp index 1235513d4e..d1965aef10 100644 --- a/src/graphics/image/Image.cpp +++ b/src/graphics/image/Image.cpp @@ -25,7 +25,6 @@ #include #include "graphics/image/stb_image.h" -#include "graphics/image/stb_image_write.h" #include "graphics/Math.h" #include "io/fs/FilePath.h" @@ -626,23 +625,6 @@ void Image::flipY() { } -bool Image::save(const fs::path & filename) const { - - if(getFormat() >= Format_Unknown) { - return false; - } - - int ret = 0; - if(filename.ext() == ".bmp") { - ret = stbi::stbi_write_bmp(filename.string().c_str(), int(getWidth()), int(getHeight()), - int(getNumChannels()), getData()); - } else { - LogError << "Unsupported file extension: " << filename.ext(); - } - - return ret != 0; -} - std::ostream & operator<<(std::ostream & os, Image::Format format) { switch(format) { case Image::Format_L8: return os << "L"; diff --git a/src/graphics/image/ImageSave.cpp b/src/graphics/image/ImageSave.cpp new file mode 100644 index 0000000000..19a9bd41fb --- /dev/null +++ b/src/graphics/image/ImageSave.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 2018 Arx Libertatis Team (see the AUTHORS file) + * + * This file is part of Arx Libertatis. + * + * Arx Libertatis is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Arx Libertatis is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Arx Libertatis. If not, see . + */ +/* Copyright: stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 + no warranty implied; use at your own risk +*/ + +#include "graphics/image/Image.h" + +#include "io/fs/FilePath.h" +#include "io/fs/FileStream.h" +#include "io/log/Logger.h" + +#include "platform/Platform.h" + +namespace { + +void writeU16LE(fs::ofstream & f, u16 x) { + char buffer[2] = { char(u8(x)), char(u8(x >> 8)) }; + f.write(buffer, 2); +} + +void writeU32LE(fs::ofstream & f, u32 x) { + char buffer[4] = { char(u8(x)), char(u8(x >> 8)), char(u8(x >> 16)), char(u8(x >> 24)) }; + f.write(buffer, 4); +} + +} // anonymous namespace + +bool Image::save(const fs::path & filename) const { + + if(getFormat() >= Format_Unknown || getWidth() == 0 || getHeight() == 0) { + return false; + } + + if(filename.ext() != ".bmp") { + LogWarning << "Unexpected file extension for BMP: " << filename.ext(); + } + + fs::ofstream f(filename, fs::fstream::out | fs::fstream::binary | fs::fstream::trunc); + if(!f.is_open()) { + return false; + } + + size_t w = getWidth(); + size_t h = getHeight(); + size_t c = getNumChannels(); + + // File header + const char magic[] = { 'B', 'M' }; + f.write(magic, sizeof(magic)); + size_t scanline_pad = size_t((-int(w) * 3) & 3); + writeU32LE(f, u32(14 + 40 + (w * 3 + scanline_pad) * h)); + writeU16LE(f, 0); + writeU16LE(f, 0); + writeU32LE(f, 14 + 40); + + // Bitmap header + writeU32LE(f, 40); + writeU32LE(f, u32(w)); + writeU32LE(f, u32(h)); + writeU16LE(f, 1); + writeU16LE(f, 24); + writeU32LE(f, 0); + writeU32LE(f, 0); + writeU32LE(f, 0); + writeU32LE(f, 0); + writeU32LE(f, 0); + writeU32LE(f, 0); + if(!f.good()) { + return false; + } + + for(size_t y = h; y != 0; y--) { + + for(size_t x = 0; x < w; x++) { + const unsigned char * d = getData() + ((y - 1) * w + x) * c; + + char buffer[3]; + switch(getFormat()) { + + case Format_L8: + case Format_A8: + case Format_L8A8: { + buffer[0] = buffer[1] = buffer[2] = char(d[0]); + break; + } + + case Format_R8G8B8: { + buffer[0] = char(d[2]); + buffer[1] = char(d[1]); + buffer[2] = char(d[0]); + break; + } + + case Format_B8G8R8: { + buffer[0] = char(d[0]); + buffer[1] = char(d[1]); + buffer[2] = char(d[2]); + break; + } + + case Format_R8G8B8A8: { + // Composite against pink background + const unsigned char bg[3] = { 255, 0, 255 }; + for(size_t k = 0; k < 3; ++k) { + buffer[k] = char(u8(bg[k] + ((d[2 - k] - bg[k]) * d[3]) / 255)); + } + break; + } + + case Format_B8G8R8A8: { + // Composite against pink background + const unsigned char bg[3] = { 255, 0, 255 }; + for(size_t k = 0; k < 3; ++k) { + buffer[k] = char(u8(bg[k] + ((d[k] - bg[k]) * d[3]) / 255)); + } + break; + } + + case Format_Unknown: + case Format_Num: { + arx_unreachable(); + } + + } + + if(!f.write(buffer, 3).good()) { + return false; + } + + } + + if(scanline_pad > 0) { + const char padding[] = { 0, 0 }; + arx_assert(scanline_pad <= sizeof(padding)); + if(!f.write(padding, scanline_pad).good()) { + return false; + } + } + + } + + return true; +} diff --git a/src/graphics/image/stb_image_write.cpp b/src/graphics/image/stb_image_write.cpp deleted file mode 100644 index ca9192c44f..0000000000 --- a/src/graphics/image/stb_image_write.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright: stbiw-0.92 - public domain - http://nothings.org/stb/stb_image_write.h - writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 - no warranty implied; use at your own risk -*/ - -#include "graphics/image/stb_image_write.h" - -#include - -#include "platform/Platform.h" - -namespace stbi { - -static size_t write2(FILE * f, int x) { - unsigned char b[2] = { (unsigned char)x, (unsigned char)(x >> 8) }; - return fwrite(b, 2, 1, f); -} - -static size_t write3(FILE * f, unsigned char a, unsigned char b, unsigned char c) { - unsigned char arr[3]; - arr[0] = a, arr[1] = b, arr[2] = c; - return fwrite(arr, 3, 1, f); -} - -static size_t write4(FILE * f, u32 x) { - unsigned char b[4] = { (unsigned char)x, (unsigned char)(x >> 8), - (unsigned char)(x >> 16), (unsigned char)(x >> 24) }; - return fwrite(b, 4, 1, f); -} - -static int write_pixels(FILE * f, int x, int y, int comp, const void * data, int scanline_pad) { - - if(y <= 0) { - return 1; - } - - for(int j = y - 1; j != -1; j--) { - - for(int i = 0; i < x; i++) { - const unsigned char * d = reinterpret_cast(data) + (j * x + i) * comp; - - switch(comp) { - - case 1: - case 2: { - if(!write3(f, d[0], d[0], d[0])) { - return 0; - } - break; - } - - case 3: { - if(!write3(f, d[2], d[1], d[0])) { - return 0; - } - break; - } - - case 4: { - // Composite against pink background - const unsigned char bg[3] = { 255, 0, 255 }; - unsigned char px[3]; - for(int k = 0; k < 3; ++k) { - px[k] = u8(bg[k] + ((d[k] - bg[k]) * d[3]) / 255); - } - if(!write3(f, px[2], px[1], px[0])) { - return 0; - } - break; - } - - default: arx_unreachable(); - - } - - } - - u32 zero = 0; - if(scanline_pad > 0 && !fwrite(&zero, size_t(scanline_pad), 1, f)) { - return 0; - } - - } - - return 1; -} - -int stbi_write_bmp(char const * filename, int w, int h, int comp, const void * data) { - - if(h < 0 || w < 0) { - return 0; - } - - FILE * f = fopen(filename, "wb"); - if(!f) { - return 0; - } - - // File header - fputc('B', f); - fputc('M', f); - int pad = (-w * 3) & 3; - write4(f, u32(14 + 40 + (w * 3 + pad) * h)); - write2(f, 0); - write2(f, 0); - write4(f, 14 + 40); - - // Bitmap header - write4(f, 40); - write4(f, u32(w)); - write4(f, u32(h)); - write2(f, 1); - write2(f, 24); - write4(f, 0); - write4(f, 0); - write4(f, 0); - write4(f, 0); - write4(f, 0); - write4(f, 0); - - int ret = write_pixels(f, w, h, comp, data, pad); - - fclose(f); - - return ret; -} - -} // namespace stbi diff --git a/src/graphics/image/stb_image_write.h b/src/graphics/image/stb_image_write.h deleted file mode 100644 index df26c289ab..0000000000 --- a/src/graphics/image/stb_image_write.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright: stbiw-0.92-arx2 - public domain - http://nothings.org/stb/stb_image_write.h - writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010 - no warranty implied; use at your own risk - -ABOUT: - - This header file is a library for writing images to C stdio. It could be - adapted to write to memory or a general streaming interface; let me know. - - The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation. This library is designed - for source code compactness and simplicitly, not optimal image file size - or run-time performance. - -USAGE: - - There are three functions, one for each image file format: - - int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); - int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); - int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); - - Each function returns 0 on failure and non-0 on success. - - The functions create an image file defined by the parameters. The image - is a rectangle of pixels stored from left-to-right, top-to-bottom. - Each pixel contains 'comp' channels of data stored interleaved with 8-bits - per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is - monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. - The *data pointer points to the first byte of the top-left-most pixel. - For PNG, "stride_in_bytes" is the distance in bytes from the first byte of - a row of pixels to the first byte of the next row of pixels. - - PNG creates output files with the same number of components as the input. - The BMP and TGA formats expand Y to RGB in the file format. BMP does not - output alpha. - - PNG supports writing rectangles of data even when the bytes storing rows of - data are not consecutive in memory (e.g. sub-rectangles of a larger image), - by supplying the stride between the beginning of adjacent rows. The other - formats do not. (Thus you cannot write a native-format BMP through the BMP - writer, both because it is in BGR order and because it may have padding - at the end of the line.) -*/ - -#ifndef ARX_GRAPHICS_IMAGE_STB_IMAGE_WRITE_H -#define ARX_GRAPHICS_IMAGE_STB_IMAGE_WRITE_H - -namespace stbi { - -int stbi_write_bmp(char const * filename, int w, int h, int comp, const void * data); - -} // namespace stbi - -#endif // ARX_GRAPHICS_IMAGE_STB_IMAGE_WRITE_H - -/* Revision history - - 0.92-arx1 (2012-08-21) - check for file errors - 0.92-arx1 (2012-08-21) - fix const correctness - 0.92 (2010-08-01) - casts to unsigned char to fix warnings - 0.91 (2010-07-17) - first public release - 0.90 first internal release -*/