diff options
Diffstat (limited to 'source/blender/imbuf/intern/util_gpu.c')
-rw-r--r-- | source/blender/imbuf/intern/util_gpu.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c new file mode 100644 index 00000000000..f150b2bf65e --- /dev/null +++ b/source/blender/imbuf/intern/util_gpu.c @@ -0,0 +1,213 @@ +/* + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * util.c + */ + +/** \file + * \ingroup imbuf + */ + +#include "imbuf.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "MEM_guardedalloc.h" + +#include "BKE_global.h" + +#include "GPU_extensions.h" +#include "GPU_texture.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +/* gpu ibuf utils */ + +void IMB_gpu_get_format(const ImBuf *ibuf, + bool high_bitdepth, + uint *r_data_format /* eGPUDataFormat */, + uint *r_texture_format /* eGPUTextureFormat */) +{ + const bool float_rect = (ibuf->rect_float != NULL); + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); + high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth); + + *r_data_format = (float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UNSIGNED_BYTE; + + if (float_rect) { + *r_texture_format = high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F; + } + else { + *r_texture_format = use_srgb ? GPU_SRGB8_A8 : GPU_RGBA8; + } +} + +/* Return false if no suitable format was found. */ +#ifdef WITH_DDS +static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format) +{ + /* For DDS we only support data, scene linear and sRGB. Converting to + * different colorspace would break the compression. */ + const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace) && + !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); + + if (ibuf->dds_data.fourcc == FOURCC_DXT1) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT1 : GPU_RGBA8_DXT1; + } + else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT3 : GPU_RGBA8_DXT3; + } + else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { + *r_texture_format = (use_srgb) ? GPU_SRGB8_A8_DXT5 : GPU_RGBA8_DXT5; + } + else { + return false; + } + return true; +} +#endif + +/** + * Apply colormanagement and scale buffer if needed. + * *r_freedata is set to true if the returned buffer need to be manually freed. + **/ +void *IMB_gpu_get_data(const ImBuf *ibuf, + const bool do_rescale, + const int rescale_size[2], + const bool compress_as_srgb, + const bool store_premultiplied, + bool *r_freedata) +{ + const bool is_float_rect = (ibuf->rect_float != NULL); + void *data_rect = (is_float_rect) ? (void *)ibuf->rect_float : (void *)ibuf->rect; + + if (is_float_rect) { + /* Float image is already in scene linear colorspace or non-color data by + * convention, no colorspace conversion needed. But we do require 4 channels + * currently. */ + if (ibuf->channels != 4 || !store_premultiplied) { + data_rect = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); + *r_freedata = true; + + if (data_rect == NULL) { + return NULL; + } + + IMB_colormanagement_imbuf_to_float_texture( + (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); + } + } + else { + /* Byte image is in original colorspace from the file. If the file is sRGB + * scene linear, or non-color data no conversion is needed. Otherwise we + * compress as scene linear + sRGB transfer function to avoid precision loss + * in common cases. + * + * We must also convert to premultiplied for correct texture interpolation + * and consistency with float images. */ + if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { + data_rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); + *r_freedata = true; + + if (data_rect == NULL) { + return NULL; + } + + /* Texture storage of images is defined by the alpha mode of the image. The + * downside of this is that there can be artifacts near alpha edges. However, + * this allows us to use sRGB texture formats and preserves color values in + * zero alpha areas, and appears generally closer to what game engines that we + * want to be compatible with do. */ + IMB_colormanagement_imbuf_to_byte_texture( + (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); + } + } + + if (do_rescale) { + uint *rect = (is_float_rect) ? NULL : (uint *)data_rect; + float *rect_float = (is_float_rect) ? (float *)data_rect : NULL; + + ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4); + IMB_scaleImBuf(scale_ibuf, UNPACK2(rescale_size)); + + data_rect = (is_float_rect) ? (void *)scale_ibuf->rect_float : (void *)scale_ibuf->rect; + *r_freedata = true; + /* Steal the rescaled buffer to avoid double free. */ + scale_ibuf->rect_float = NULL; + scale_ibuf->rect = NULL; + IMB_freeImBuf(scale_ibuf); + } + return data_rect; +} + +GPUTexture *IMB_create_gpu_texture(ImBuf *ibuf, bool use_high_bitdepth, bool use_premult) +{ + GPUTexture *tex = NULL; + int size[2] = {GPU_texture_size_with_limit(ibuf->x), GPU_texture_size_with_limit(ibuf->y)}; + bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]); + +#ifdef WITH_DDS + if (ibuf->ftype == IMB_FTYPE_DDS) { + eGPUTextureFormat compressed_format; + if (!IMB_gpu_get_compressed_format(ibuf, &compressed_format)) { + fprintf(stderr, "Unable to find a suitable DXT compression,"); + } + else if (do_rescale) { + fprintf(stderr, "Unable to load DXT image resolution,"); + } + else if (!is_power_of_2_i(ibuf->x) || !is_power_of_2_i(ibuf->y)) { + fprintf(stderr, "Unable to load non-power-of-two DXT image resolution,"); + } + else { + tex = GPU_texture_create_compressed( + ibuf->x, ibuf->y, ibuf->dds_data.nummipmaps, compressed_format, ibuf->dds_data.data); + + if (tex != NULL) { + return tex; + } + else { + fprintf(stderr, "ST3C support not found,"); + } + } + /* Fallback to uncompressed texture. */ + fprintf(stderr, " falling back to uncompressed.\n"); + } +#endif + + eGPUDataFormat data_format; + eGPUTextureFormat tex_format; + IMB_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format); + + const bool compress_as_srgb = (tex_format == GPU_SRGB8_A8); + bool freebuf = false; + + void *data = IMB_gpu_get_data(ibuf, do_rescale, size, compress_as_srgb, use_premult, &freebuf); + + /* Create Texture. */ + tex = GPU_texture_create_nD(UNPACK2(size), 0, 2, data, tex_format, data_format, 0, false, NULL); + + GPU_texture_anisotropic_filter(tex, true); + + if (freebuf) { + MEM_freeN(data); + } + + return tex; +}
\ No newline at end of file |