diff options
Diffstat (limited to 'core/image.cpp')
-rw-r--r-- | core/image.cpp | 151 |
1 files changed, 94 insertions, 57 deletions
diff --git a/core/image.cpp b/core/image.cpp index 07e705265d..58f49d69e6 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -366,6 +366,8 @@ int Image::get_mipmap_count() const { template <uint32_t read_bytes, bool read_alpha, uint32_t write_bytes, bool write_alpha, bool read_gray, bool write_gray> static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p_dst) { + uint32_t max_bytes = MAX(read_bytes, write_bytes); + for (int y = 0; y < p_height; y++) { for (int x = 0; x < p_width; x++) { @@ -379,7 +381,8 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p rgba[1] = rofs[0]; rgba[2] = rofs[0]; } else { - for (uint32_t i = 0; i < MAX(read_bytes, write_bytes); i++) { + + for (uint32_t i = 0; i < max_bytes; i++) { rgba[i] = (i < read_bytes) ? rofs[i] : 0; } @@ -937,7 +940,7 @@ bool Image::_can_modify(Format p_format) const { return p_format <= FORMAT_RGBE9995; } -template <int CC> +template <int CC, bool renormalize> static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height) { //fast power of 2 mipmap generation @@ -963,6 +966,19 @@ static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t dst_ptr[j] = val >> 2; } + if (renormalize) { + Vector3 n(dst_ptr[0] / 255.0, dst_ptr[1] / 255.0, dst_ptr[2] / 255.0); + n *= 2.0; + n -= Vector3(1, 1, 1); + n.normalize(); + n += Vector3(1, 1, 1); + n *= 0.5; + n *= 255; + dst_ptr[0] = CLAMP(int(n.x), 0, 255); + dst_ptr[1] = CLAMP(int(n.y), 0, 255); + dst_ptr[2] = CLAMP(int(n.z), 0, 255); + } + dst_ptr += CC; rup_ptr += CC * 2; rdown_ptr += CC * 2; @@ -1045,11 +1061,11 @@ void Image::shrink_x2() { switch (format) { case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_LA8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RG8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGB8: _generate_po2_mipmap<3>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGBA8: _generate_po2_mipmap<4>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_R8: _generate_po2_mipmap<1, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_LA8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RG8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGB8: _generate_po2_mipmap<3, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGBA8: _generate_po2_mipmap<4, false>(r.ptr(), w.ptr(), width, height); break; default: {} } } @@ -1060,7 +1076,7 @@ void Image::shrink_x2() { } } -Error Image::generate_mipmaps() { +Error Image::generate_mipmaps(bool p_renormalize) { if (!_can_modify(format)) { ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats."); @@ -1077,61 +1093,40 @@ Error Image::generate_mipmaps() { PoolVector<uint8_t>::Write wp = data.write(); - if (next_power_of_2(width) == uint32_t(width) && next_power_of_2(height) == uint32_t(height)) { - //use fast code for powers of 2 - int prev_ofs = 0; - int prev_h = height; - int prev_w = width; + int prev_ofs = 0; + int prev_h = height; + int prev_w = width; - for (int i = 1; i < mmcount; i++) { + for (int i = 1; i < mmcount; i++) { - int ofs, w, h; - _get_mipmap_offset_and_size(i, ofs, w, h); + int ofs, w, h; + _get_mipmap_offset_and_size(i, ofs, w, h); - switch (format) { + switch (format) { - case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - case FORMAT_LA8: - case FORMAT_RG8: _generate_po2_mipmap<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - case FORMAT_RGB8: _generate_po2_mipmap<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - case FORMAT_RGBA8: _generate_po2_mipmap<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - default: {} - } + case FORMAT_L8: + case FORMAT_R8: _generate_po2_mipmap<1, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_LA8: + case FORMAT_RG8: _generate_po2_mipmap<2, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_RGB8: + if (p_renormalize) + _generate_po2_mipmap<3, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + else + _generate_po2_mipmap<3, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); - prev_ofs = ofs; - prev_w = w; - prev_h = h; + break; + case FORMAT_RGBA8: + if (p_renormalize) + _generate_po2_mipmap<4, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + else + _generate_po2_mipmap<4, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + break; + default: {} } - } else { - //use slow code.. - - //use bilinear filtered code for non powers of 2 - int prev_ofs = 0; - int prev_h = height; - int prev_w = width; - - for (int i = 1; i < mmcount; i++) { - - int ofs, w, h; - _get_mipmap_offset_and_size(i, ofs, w, h); - - switch (format) { - - case FORMAT_L8: - case FORMAT_R8: _scale_bilinear<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break; - case FORMAT_LA8: - case FORMAT_RG8: _scale_bilinear<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break; - case FORMAT_RGB8: _scale_bilinear<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break; - case FORMAT_RGBA8: _scale_bilinear<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break; - default: {} - } - - prev_ofs = ofs; - prev_w = w; - prev_h = h; - } + prev_ofs = ofs; + prev_w = w; + prev_h = h; } mipmaps = true; @@ -2249,7 +2244,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("crop", "width", "height"), &Image::crop); ClassDB::bind_method(D_METHOD("flip_x"), &Image::flip_x); ClassDB::bind_method(D_METHOD("flip_y"), &Image::flip_y); - ClassDB::bind_method(D_METHOD("generate_mipmaps"), &Image::generate_mipmaps); + ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps); ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::_create_empty); @@ -2271,6 +2266,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha); ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear); ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy); + ClassDB::bind_method(D_METHOD("bumpmap_to_normalmap", "bump_scale"), &Image::bumpmap_to_normalmap, DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect); ClassDB::bind_method(D_METHOD("blit_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blit_rect_mask); @@ -2379,6 +2375,47 @@ void Image::normalmap_to_xy() { convert(Image::FORMAT_LA8); } +void Image::bumpmap_to_normalmap(float bump_scale) { + ERR_FAIL_COND(!_can_modify(format)); + convert(Image::FORMAT_RF); + + PoolVector<uint8_t> result_image; //rgba output + result_image.resize(width * height * 4); + + { + PoolVector<uint8_t>::Read rp = data.read(); + PoolVector<uint8_t>::Write wp = result_image.write(); + + unsigned char *write_ptr = wp.ptr(); + float *read_ptr = (float *)rp.ptr(); + + for (int ty = 0; ty < height; ty++) { + int py = ty + 1; + if (py >= height) py -= height; + + for (int tx = 0; tx < width; tx++) { + int px = tx + 1; + if (px >= width) px -= width; + float here = read_ptr[ty * width + tx]; + float to_right = read_ptr[ty * width + px]; + float above = read_ptr[py * width + tx]; + Vector3 up = Vector3(0, 1, (here - above) * bump_scale); + Vector3 across = Vector3(1, 0, (to_right - here) * bump_scale); + + Vector3 normal = across.cross(up); + normal.normalize(); + + write_ptr[((ty * width + tx) << 2) + 0] = (127.5 + normal.x * 127.5); + write_ptr[((ty * width + tx) << 2) + 1] = (127.5 + normal.y * 127.5); + write_ptr[((ty * width + tx) << 2) + 2] = (127.5 + normal.z * 127.5); + write_ptr[((ty * width + tx) << 2) + 3] = 255; + } + } + } + format = FORMAT_RGBA8; + data = result_image; +} + void Image::srgb_to_linear() { if (data.size() == 0) |