From: José Fonseca Date: Fri, 7 Dec 2012 07:33:05 +0000 (+0000) Subject: image: Move image code into its own module. X-Git-Url: https://git.cworth.org/git?p=apitrace;a=commitdiff_plain;h=e7102bf755210f9e215fb048b637dda6bab96334 image: Move image code into its own module. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c4094f..e31cb54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,8 +206,6 @@ set (PNG_DEFINITIONS "") set (PNG_LIBRARIES png_bundled) add_subdirectory (thirdparty/libpng EXCLUDE_FROM_ALL) -include_directories (${PNG_INCLUDE_DIR}) -add_definitions (${PNG_DEFINITIONS}) if (MSVC) add_subdirectory (thirdparty/getopt EXCLUDE_FROM_ALL) @@ -321,10 +319,6 @@ add_library (common STATIC common/trace_writer_model.cpp common/trace_loader.cpp common/trace_profiler.cpp - common/image.cpp - common/image_bmp.cpp - common/image_pnm.cpp - common/image_png.cpp common/trace_option.cpp common/${os} ) @@ -348,6 +342,7 @@ endif () add_subdirectory (dispatch) add_subdirectory (helpers) add_subdirectory (wrappers) +add_subdirectory (image) add_subdirectory (retrace) diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 521886b..a01d3ae 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -16,7 +16,6 @@ add_executable (apitrace target_link_libraries (apitrace common - ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${SNAPPY_LIBRARIES} ${GETOPT_LIBRARIES} diff --git a/common/image.cpp b/common/image.cpp deleted file mode 100644 index e692313..0000000 --- a/common/image.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * 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 -#include - -#include - -#include "image.hpp" - - -namespace image { - - -double Image::compare(Image &ref) -{ - if (width != ref.width || - height != ref.height || - channels < 3 || - ref.channels < 3) { - return 0.0; - } - - // Ignore missing alpha when comparing RGB w/ RGBA, but enforce an equal - // number of channels otherwise. - unsigned minChannels = std::min(channels, ref.channels); - if (channels != ref.channels && minChannels < 3) { - return 0.0; - } - - const unsigned char *pSrc = start(); - const unsigned char *pRef = ref.start(); - - unsigned long long error = 0; - for (unsigned y = 0; y < height; ++y) { - for (unsigned x = 0; x < width; ++x) { - // FIXME: Ignore alpha channel until we are able to pick a visual - // that matches the traces - for (unsigned c = 0; c < minChannels; ++c) { - int delta = pSrc[x*channels + c] - pRef[x*ref.channels + c]; - error += delta*delta; - } - } - - pSrc += stride(); - pRef += ref.stride(); - } - - double numerator = error*2 + 1; - double denominator = height*width*minChannels*255ULL*255ULL*2; - double quotient = numerator/denominator; - - // Precision in bits - double precision = -log(quotient)/log(2.0); - - return precision; -} - - -} /* namespace image */ diff --git a/common/image.hpp b/common/image.hpp deleted file mode 100644 index 7dd18c1..0000000 --- a/common/image.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/************************************************************************** - * - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * 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. - * - **************************************************************************/ - -/* - * Image I/O. - */ - -#ifndef _IMAGE_HPP_ -#define _IMAGE_HPP_ - - -#include - - -namespace image { - - -class Image { -public: - unsigned width; - unsigned height; - unsigned channels; - - // Flipped vertically or not - bool flipped; - - // Pixels in RGBA format - unsigned char *pixels; - - inline Image(unsigned w, unsigned h, unsigned c = 4, bool f = false) : - width(w), - height(h), - channels(c), - flipped(f), - pixels(new unsigned char[h*w*c]) - {} - - inline ~Image() { - delete [] pixels; - } - - inline unsigned char *start(void) { - return flipped ? pixels + (height - 1)*width*channels : pixels; - } - - inline const unsigned char *start(void) const { - return flipped ? pixels + (height - 1)*width*channels : pixels; - } - - inline unsigned char *end(void) { - return flipped ? pixels - width*channels : pixels + height*width*channels; - } - - inline const unsigned char *end(void) const { - return flipped ? pixels - width*channels : pixels + height*width*channels; - } - - inline signed stride(void) const { - return flipped ? -(signed)(width*channels) : width*channels; - } - - bool writeBMP(const char *filename) const; - - void writePNM(std::ostream &os, const char *comment = NULL) const; - - inline bool writePNM(const char *filename, const char *comment = NULL) const { - std::ofstream os(filename, std::ofstream::binary); - if (!os) { - return false; - } - writePNM(os, comment); - return true; - } - - bool - writePNG(std::ostream &os) const; - - inline bool - writePNG(const char *filename) const { - std::ofstream os(filename, std::ofstream::binary); - if (!os) { - return false; - } - return writePNG(os); - } - - double compare(Image &ref); -}; - - -Image * -readPNG(const char *filename); - -const char * -readPNMHeader(const char *buffer, size_t size, unsigned *channels, unsigned *width, unsigned *height); - - -} /* namespace image */ - - -#endif /* _IMAGE_HPP_ */ diff --git a/common/image_bmp.cpp b/common/image_bmp.cpp deleted file mode 100644 index e0c6428..0000000 --- a/common/image_bmp.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * 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 -#include - -#include "image.hpp" - - -namespace image { - - -#pragma pack(push,2) -struct FileHeader { - uint16_t bfType; - uint32_t bfSize; - uint16_t bfReserved1; - uint16_t bfReserved2; - uint32_t bfOffBits; -}; -#pragma pack(pop) - -struct InfoHeader { - uint32_t biSize; - int32_t biWidth; - int32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - int32_t biXPelsPerMeter; - int32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; -}; - -struct Pixel { - uint8_t rgbBlue; - uint8_t rgbGreen; - uint8_t rgbRed; - uint8_t rgbAlpha; -}; - - -bool -Image::writeBMP(const char *filename) const { - assert(channels == 4); - - struct FileHeader bmfh; - struct InfoHeader bmih; - unsigned x, y; - - bmfh.bfType = 0x4d42; - bmfh.bfSize = 14 + 40 + height*width*4; - bmfh.bfReserved1 = 0; - bmfh.bfReserved2 = 0; - bmfh.bfOffBits = 14 + 40; - - bmih.biSize = 40; - bmih.biWidth = width; - bmih.biHeight = height; - bmih.biPlanes = 1; - bmih.biBitCount = 32; - bmih.biCompression = 0; - bmih.biSizeImage = height*width*4; - bmih.biXPelsPerMeter = 0; - bmih.biYPelsPerMeter = 0; - bmih.biClrUsed = 0; - bmih.biClrImportant = 0; - - std::ofstream stream(filename, std::ofstream::binary); - - if (!stream) { - return false; - } - - stream.write((const char *)&bmfh, 14); - stream.write((const char *)&bmih, 40); - - unsigned stride = width*4; - - if (flipped) { - for (y = 0; y < height; ++y) { - const unsigned char *ptr = pixels + y * stride; - for (x = 0; x < width; ++x) { - struct Pixel pixel; - pixel.rgbRed = ptr[x*4 + 0]; - pixel.rgbGreen = ptr[x*4 + 1]; - pixel.rgbBlue = ptr[x*4 + 2]; - pixel.rgbAlpha = ptr[x*4 + 3]; - stream.write((const char *)&pixel, 4); - } - } - } else { - y = height; - while (y--) { - const unsigned char *ptr = pixels + y * stride; - for (x = 0; x < width; ++x) { - struct Pixel pixel; - pixel.rgbRed = ptr[x*4 + 0]; - pixel.rgbGreen = ptr[x*4 + 1]; - pixel.rgbBlue = ptr[x*4 + 2]; - pixel.rgbAlpha = ptr[x*4 + 3]; - stream.write((const char *)&pixel, 4); - } - } - } - - stream.close(); - - return true; -} - - -} /* namespace image */ diff --git a/common/image_png.cpp b/common/image_png.cpp deleted file mode 100644 index dba07d4..0000000 --- a/common/image_png.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * 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 -#include - -#include -#include -#include - -#include - -#include "image.hpp" - - -namespace image { - - -static const int png_compression_level = Z_BEST_SPEED; - - -static void -pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) -{ - std::ostream *os = (std::ostream *) png_get_io_ptr(png_ptr); - os->write((const char *)data, length); -} - -bool -Image::writePNG(std::ostream &os) const -{ - png_structp png_ptr; - png_infop info_ptr; - int color_type; - - switch (channels) { - case 4: - color_type = PNG_COLOR_TYPE_RGB_ALPHA; - break; - case 3: - color_type = PNG_COLOR_TYPE_RGB; - break; - case 2: - color_type = PNG_COLOR_TYPE_GRAY_ALPHA; - break; - case 1: - color_type = PNG_COLOR_TYPE_GRAY; - break; - default: - assert(0); - goto no_png; - } - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - goto no_png; - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_write_struct(&png_ptr, NULL); - goto no_png; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, &info_ptr); - goto no_png; - } - - png_set_write_fn(png_ptr, &os, pngWriteCallback, NULL); - - png_set_IHDR(png_ptr, info_ptr, width, height, 8, - color_type, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_set_compression_level(png_ptr, png_compression_level); - - png_write_info(png_ptr, info_ptr); - - if (!flipped) { - for (unsigned y = 0; y < height; ++y) { - png_bytep row = (png_bytep)(pixels + y*width*channels); - png_write_rows(png_ptr, &row, 1); - } - } else { - unsigned y = height; - while (y--) { - png_bytep row = (png_bytep)(pixels + y*width*channels); - png_write_rows(png_ptr, &row, 1); - } - } - - png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - - return true; - -no_png: - return false; -} - - -Image * -readPNG(const char *filename) -{ - FILE *fp; - png_structp png_ptr; - png_infop info_ptr; - png_infop end_info; - Image *image; - - fp = fopen(filename, "rb"); - if (!fp) - goto no_fp; - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - goto no_png; - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_read_struct(&png_ptr, NULL, NULL); - goto no_png; - } - - end_info = png_create_info_struct(png_ptr); - if (!end_info) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - goto no_png; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - goto no_png; - } - - png_init_io(png_ptr, fp); - - png_read_info(png_ptr, info_ptr); - - png_uint_32 width, height; - int bit_depth, color_type, interlace_type, compression_type, filter_method; - - png_get_IHDR(png_ptr, info_ptr, - &width, &height, - &bit_depth, &color_type, &interlace_type, - &compression_type, &filter_method); - - image = new Image(width, height); - if (!image) - goto no_image; - - /* Convert to RGBA8 */ - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand_gray_1_2_4_to_8(png_ptr); - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(png_ptr); - if (bit_depth == 16) - png_set_strip_16(png_ptr); - - for (unsigned y = 0; y < height; ++y) { - png_bytep row = (png_bytep)(image->pixels + y*width*4); - png_read_row(png_ptr, row, NULL); - } - - png_read_end(png_ptr, info_ptr); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - fclose(fp); - return image; - -no_image: - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); -no_png: - fclose(fp); -no_fp: - return NULL; -} - - - -} /* namespace image */ diff --git a/common/image_pnm.cpp b/common/image_pnm.cpp deleted file mode 100644 index f9cd05d..0000000 --- a/common/image_pnm.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * 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 -#include -#include -#include - -#include "image.hpp" - - -namespace image { - -/** - * http://en.wikipedia.org/wiki/Netpbm_format - * http://netpbm.sourceforge.net/doc/ppm.html - */ -void -Image::writePNM(std::ostream &os, const char *comment) const { - assert(channels == 1 || channels >= 3); - - os << (channels == 1 ? "P5" : "P6") << "\n"; - if (comment) { - os << "#" << comment << "\n"; - } - os << width << " " << height << "\n"; - os << "255" << "\n"; - - const unsigned char *row; - - if (channels == 1 || channels == 3) { - for (row = start(); row != end(); row += stride()) { - os.write((const char *)row, width*channels); - } - } else { - unsigned char *tmp = new unsigned char[width*3]; - if (channels == 4) { - for (row = start(); row != end(); row += stride()) { - const uint32_t *src = (const uint32_t *)row; - uint32_t *dst = (uint32_t *)tmp; - unsigned x; - for (x = 0; x + 4 <= width; x += 4) { - /* - * It's much faster to access dwords than bytes. - * - * FIXME: Big-endian version. - */ - - uint32_t rgba0 = *src++ & 0xffffff; - uint32_t rgba1 = *src++ & 0xffffff; - uint32_t rgba2 = *src++ & 0xffffff; - uint32_t rgba3 = *src++ & 0xffffff; - uint32_t rgb0 = rgba0 - | (rgba1 << 24); - uint32_t rgb1 = (rgba1 >> 8) - | (rgba2 << 16); - uint32_t rgb2 = (rgba2 >> 16) - | (rgba3 << 8); - *dst++ = rgb0; - *dst++ = rgb1; - *dst++ = rgb2; - } - for (; x < width; ++x) { - tmp[x*3 + 0] = row[x*4 + 0]; - tmp[x*3 + 1] = row[x*4 + 1]; - tmp[x*3 + 2] = row[x*4 + 2]; - } - os.write((const char *)tmp, width*3); - } - } else if (channels == 2) { - for (row = start(); row != end(); row += stride()) { - const unsigned char *src = row; - unsigned char *dst = tmp; - for (unsigned x = 0; x < width; ++x) { - *dst++ = *src++; - *dst++ = *src++; - *dst++ = 0; - } - os.write((const char *)tmp, width*3); - } - } else { - assert(0); - } - delete [] tmp; - } -} - -const char * -readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigned *width, unsigned *height) -{ - *channels = 0; - *width = 0; - *height = 0; - - const char *currentBuffer = buffer; - const char *nextBuffer; - - // parse number of channels - int scannedChannels = sscanf(currentBuffer, "P%d\n", channels); - if (scannedChannels != 1) { // validate scanning of channels - // invalid channel line - return buffer; - } - // convert channel token to number of channels - *channels = (*channels == 5) ? 1 : 3; - - // advance past channel line - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - bufferSize -= nextBuffer - currentBuffer; - currentBuffer = nextBuffer; - - // skip over optional comment - if (*currentBuffer == '#') { - // advance past comment line - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - bufferSize -= nextBuffer - currentBuffer; - currentBuffer = nextBuffer; - } - - // parse dimensions of image - int scannedDimensions = sscanf(currentBuffer, "%d %d\n", width, height); - if (scannedDimensions != 2) { // validate scanning of dimensions - // invalid dimension line - return buffer; - } - - // advance past dimension line - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - bufferSize -= nextBuffer - currentBuffer; - currentBuffer = nextBuffer; - - // skip over "255\n" at end of header - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - - // return start of image data - return nextBuffer; -} - -} /* namespace image */ diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index ee18d81..773d344 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -58,7 +58,13 @@ QT4_WRAP_UI(qapitrace_UIS_H ${qapitrace_UIS}) #add_app_icon(qapitrace_SRCS ../icons/hi*-qapitrace.png) link_directories(${LINK_DIRECTORIES} ${QJSON_LIBRARY_DIRS}) -include_directories(${QT_INCLUDES} ${QJSON_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/..) +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR} + ${QJSON_INCLUDE_DIR} + ${QT_INCLUDES} +) if (WIN32) # Use Windows subsystem (i.e., no console). @@ -68,8 +74,8 @@ endif () add_executable(qapitrace ${qapitrace_SUBSYSTEM} ${qapitrace_SRCS} ${qapitrace_UIS_H}) target_link_libraries (qapitrace + image common - ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${SNAPPY_LIBRARIES} ${QJSON_LIBRARIES} diff --git a/gui/retracer.cpp b/gui/retracer.cpp index 213704b..c6698f7 100644 --- a/gui/retracer.cpp +++ b/gui/retracer.cpp @@ -3,7 +3,7 @@ #include "apitracecall.h" #include "thumbnail.h" -#include "image.hpp" +#include "image/image.hpp" #include "trace_profiler.hpp" diff --git a/image/image.cpp b/image/image.cpp new file mode 100644 index 0000000..e692313 --- /dev/null +++ b/image/image.cpp @@ -0,0 +1,84 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * 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 +#include + +#include + +#include "image.hpp" + + +namespace image { + + +double Image::compare(Image &ref) +{ + if (width != ref.width || + height != ref.height || + channels < 3 || + ref.channels < 3) { + return 0.0; + } + + // Ignore missing alpha when comparing RGB w/ RGBA, but enforce an equal + // number of channels otherwise. + unsigned minChannels = std::min(channels, ref.channels); + if (channels != ref.channels && minChannels < 3) { + return 0.0; + } + + const unsigned char *pSrc = start(); + const unsigned char *pRef = ref.start(); + + unsigned long long error = 0; + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + // FIXME: Ignore alpha channel until we are able to pick a visual + // that matches the traces + for (unsigned c = 0; c < minChannels; ++c) { + int delta = pSrc[x*channels + c] - pRef[x*ref.channels + c]; + error += delta*delta; + } + } + + pSrc += stride(); + pRef += ref.stride(); + } + + double numerator = error*2 + 1; + double denominator = height*width*minChannels*255ULL*255ULL*2; + double quotient = numerator/denominator; + + // Precision in bits + double precision = -log(quotient)/log(2.0); + + return precision; +} + + +} /* namespace image */ diff --git a/image/image.hpp b/image/image.hpp new file mode 100644 index 0000000..7dd18c1 --- /dev/null +++ b/image/image.hpp @@ -0,0 +1,123 @@ +/************************************************************************** + * + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * 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. + * + **************************************************************************/ + +/* + * Image I/O. + */ + +#ifndef _IMAGE_HPP_ +#define _IMAGE_HPP_ + + +#include + + +namespace image { + + +class Image { +public: + unsigned width; + unsigned height; + unsigned channels; + + // Flipped vertically or not + bool flipped; + + // Pixels in RGBA format + unsigned char *pixels; + + inline Image(unsigned w, unsigned h, unsigned c = 4, bool f = false) : + width(w), + height(h), + channels(c), + flipped(f), + pixels(new unsigned char[h*w*c]) + {} + + inline ~Image() { + delete [] pixels; + } + + inline unsigned char *start(void) { + return flipped ? pixels + (height - 1)*width*channels : pixels; + } + + inline const unsigned char *start(void) const { + return flipped ? pixels + (height - 1)*width*channels : pixels; + } + + inline unsigned char *end(void) { + return flipped ? pixels - width*channels : pixels + height*width*channels; + } + + inline const unsigned char *end(void) const { + return flipped ? pixels - width*channels : pixels + height*width*channels; + } + + inline signed stride(void) const { + return flipped ? -(signed)(width*channels) : width*channels; + } + + bool writeBMP(const char *filename) const; + + void writePNM(std::ostream &os, const char *comment = NULL) const; + + inline bool writePNM(const char *filename, const char *comment = NULL) const { + std::ofstream os(filename, std::ofstream::binary); + if (!os) { + return false; + } + writePNM(os, comment); + return true; + } + + bool + writePNG(std::ostream &os) const; + + inline bool + writePNG(const char *filename) const { + std::ofstream os(filename, std::ofstream::binary); + if (!os) { + return false; + } + return writePNG(os); + } + + double compare(Image &ref); +}; + + +Image * +readPNG(const char *filename); + +const char * +readPNMHeader(const char *buffer, size_t size, unsigned *channels, unsigned *width, unsigned *height); + + +} /* namespace image */ + + +#endif /* _IMAGE_HPP_ */ diff --git a/image/image_bmp.cpp b/image/image_bmp.cpp new file mode 100644 index 0000000..e0c6428 --- /dev/null +++ b/image/image_bmp.cpp @@ -0,0 +1,139 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * 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 +#include + +#include "image.hpp" + + +namespace image { + + +#pragma pack(push,2) +struct FileHeader { + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +}; +#pragma pack(pop) + +struct InfoHeader { + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +}; + +struct Pixel { + uint8_t rgbBlue; + uint8_t rgbGreen; + uint8_t rgbRed; + uint8_t rgbAlpha; +}; + + +bool +Image::writeBMP(const char *filename) const { + assert(channels == 4); + + struct FileHeader bmfh; + struct InfoHeader bmih; + unsigned x, y; + + bmfh.bfType = 0x4d42; + bmfh.bfSize = 14 + 40 + height*width*4; + bmfh.bfReserved1 = 0; + bmfh.bfReserved2 = 0; + bmfh.bfOffBits = 14 + 40; + + bmih.biSize = 40; + bmih.biWidth = width; + bmih.biHeight = height; + bmih.biPlanes = 1; + bmih.biBitCount = 32; + bmih.biCompression = 0; + bmih.biSizeImage = height*width*4; + bmih.biXPelsPerMeter = 0; + bmih.biYPelsPerMeter = 0; + bmih.biClrUsed = 0; + bmih.biClrImportant = 0; + + std::ofstream stream(filename, std::ofstream::binary); + + if (!stream) { + return false; + } + + stream.write((const char *)&bmfh, 14); + stream.write((const char *)&bmih, 40); + + unsigned stride = width*4; + + if (flipped) { + for (y = 0; y < height; ++y) { + const unsigned char *ptr = pixels + y * stride; + for (x = 0; x < width; ++x) { + struct Pixel pixel; + pixel.rgbRed = ptr[x*4 + 0]; + pixel.rgbGreen = ptr[x*4 + 1]; + pixel.rgbBlue = ptr[x*4 + 2]; + pixel.rgbAlpha = ptr[x*4 + 3]; + stream.write((const char *)&pixel, 4); + } + } + } else { + y = height; + while (y--) { + const unsigned char *ptr = pixels + y * stride; + for (x = 0; x < width; ++x) { + struct Pixel pixel; + pixel.rgbRed = ptr[x*4 + 0]; + pixel.rgbGreen = ptr[x*4 + 1]; + pixel.rgbBlue = ptr[x*4 + 2]; + pixel.rgbAlpha = ptr[x*4 + 3]; + stream.write((const char *)&pixel, 4); + } + } + } + + stream.close(); + + return true; +} + + +} /* namespace image */ diff --git a/image/image_png.cpp b/image/image_png.cpp new file mode 100644 index 0000000..dba07d4 --- /dev/null +++ b/image/image_png.cpp @@ -0,0 +1,206 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * 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 +#include + +#include +#include +#include + +#include + +#include "image.hpp" + + +namespace image { + + +static const int png_compression_level = Z_BEST_SPEED; + + +static void +pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) +{ + std::ostream *os = (std::ostream *) png_get_io_ptr(png_ptr); + os->write((const char *)data, length); +} + +bool +Image::writePNG(std::ostream &os) const +{ + png_structp png_ptr; + png_infop info_ptr; + int color_type; + + switch (channels) { + case 4: + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + break; + case 3: + color_type = PNG_COLOR_TYPE_RGB; + break; + case 2: + color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + case 1: + color_type = PNG_COLOR_TYPE_GRAY; + break; + default: + assert(0); + goto no_png; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto no_png; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, NULL); + goto no_png; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + goto no_png; + } + + png_set_write_fn(png_ptr, &os, pngWriteCallback, NULL); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_set_compression_level(png_ptr, png_compression_level); + + png_write_info(png_ptr, info_ptr); + + if (!flipped) { + for (unsigned y = 0; y < height; ++y) { + png_bytep row = (png_bytep)(pixels + y*width*channels); + png_write_rows(png_ptr, &row, 1); + } + } else { + unsigned y = height; + while (y--) { + png_bytep row = (png_bytep)(pixels + y*width*channels); + png_write_rows(png_ptr, &row, 1); + } + } + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + return true; + +no_png: + return false; +} + + +Image * +readPNG(const char *filename) +{ + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + png_infop end_info; + Image *image; + + fp = fopen(filename, "rb"); + if (!fp) + goto no_fp; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto no_png; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + goto no_png; + } + + end_info = png_create_info_struct(png_ptr); + if (!end_info) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + goto no_png; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + goto no_png; + } + + png_init_io(png_ptr, fp); + + png_read_info(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, compression_type, filter_method; + + png_get_IHDR(png_ptr, info_ptr, + &width, &height, + &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_method); + + image = new Image(width, height); + if (!image) + goto no_image; + + /* Convert to RGBA8 */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + for (unsigned y = 0; y < height; ++y) { + png_bytep row = (png_bytep)(image->pixels + y*width*4); + png_read_row(png_ptr, row, NULL); + } + + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return image; + +no_image: + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); +no_png: + fclose(fp); +no_fp: + return NULL; +} + + + +} /* namespace image */ diff --git a/image/image_pnm.cpp b/image/image_pnm.cpp new file mode 100644 index 0000000..f9cd05d --- /dev/null +++ b/image/image_pnm.cpp @@ -0,0 +1,163 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * 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 +#include +#include +#include + +#include "image.hpp" + + +namespace image { + +/** + * http://en.wikipedia.org/wiki/Netpbm_format + * http://netpbm.sourceforge.net/doc/ppm.html + */ +void +Image::writePNM(std::ostream &os, const char *comment) const { + assert(channels == 1 || channels >= 3); + + os << (channels == 1 ? "P5" : "P6") << "\n"; + if (comment) { + os << "#" << comment << "\n"; + } + os << width << " " << height << "\n"; + os << "255" << "\n"; + + const unsigned char *row; + + if (channels == 1 || channels == 3) { + for (row = start(); row != end(); row += stride()) { + os.write((const char *)row, width*channels); + } + } else { + unsigned char *tmp = new unsigned char[width*3]; + if (channels == 4) { + for (row = start(); row != end(); row += stride()) { + const uint32_t *src = (const uint32_t *)row; + uint32_t *dst = (uint32_t *)tmp; + unsigned x; + for (x = 0; x + 4 <= width; x += 4) { + /* + * It's much faster to access dwords than bytes. + * + * FIXME: Big-endian version. + */ + + uint32_t rgba0 = *src++ & 0xffffff; + uint32_t rgba1 = *src++ & 0xffffff; + uint32_t rgba2 = *src++ & 0xffffff; + uint32_t rgba3 = *src++ & 0xffffff; + uint32_t rgb0 = rgba0 + | (rgba1 << 24); + uint32_t rgb1 = (rgba1 >> 8) + | (rgba2 << 16); + uint32_t rgb2 = (rgba2 >> 16) + | (rgba3 << 8); + *dst++ = rgb0; + *dst++ = rgb1; + *dst++ = rgb2; + } + for (; x < width; ++x) { + tmp[x*3 + 0] = row[x*4 + 0]; + tmp[x*3 + 1] = row[x*4 + 1]; + tmp[x*3 + 2] = row[x*4 + 2]; + } + os.write((const char *)tmp, width*3); + } + } else if (channels == 2) { + for (row = start(); row != end(); row += stride()) { + const unsigned char *src = row; + unsigned char *dst = tmp; + for (unsigned x = 0; x < width; ++x) { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = 0; + } + os.write((const char *)tmp, width*3); + } + } else { + assert(0); + } + delete [] tmp; + } +} + +const char * +readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigned *width, unsigned *height) +{ + *channels = 0; + *width = 0; + *height = 0; + + const char *currentBuffer = buffer; + const char *nextBuffer; + + // parse number of channels + int scannedChannels = sscanf(currentBuffer, "P%d\n", channels); + if (scannedChannels != 1) { // validate scanning of channels + // invalid channel line + return buffer; + } + // convert channel token to number of channels + *channels = (*channels == 5) ? 1 : 3; + + // advance past channel line + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + bufferSize -= nextBuffer - currentBuffer; + currentBuffer = nextBuffer; + + // skip over optional comment + if (*currentBuffer == '#') { + // advance past comment line + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + bufferSize -= nextBuffer - currentBuffer; + currentBuffer = nextBuffer; + } + + // parse dimensions of image + int scannedDimensions = sscanf(currentBuffer, "%d %d\n", width, height); + if (scannedDimensions != 2) { // validate scanning of dimensions + // invalid dimension line + return buffer; + } + + // advance past dimension line + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + bufferSize -= nextBuffer - currentBuffer; + currentBuffer = nextBuffer; + + // skip over "255\n" at end of header + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + + // return start of image data + return nextBuffer; +} + +} /* namespace image */ diff --git a/retrace/CMakeLists.txt b/retrace/CMakeLists.txt index 5a2a608..c6a364f 100644 --- a/retrace/CMakeLists.txt +++ b/retrace/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories ( ${CMAKE_SOURCE_DIR}/helpers ${CMAKE_BINARY_DIR}/dispatch ${CMAKE_SOURCE_DIR}/dispatch + ${CMAKE_SOURCE_DIR}/image ) add_definitions (-DRETRACE) @@ -39,8 +40,8 @@ add_library (retrace_common STATIC json.cpp ) target_link_libraries (retrace_common + image common - ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${SNAPPY_LIBRARIES} ${GETOPT_LIBRARIES}