/* * Portions of this file are copyright Rebirth contributors and licensed as * described in COPYING.txt. * Portions of this file are copyright Parallax Software and licensed * according to the Parallax license below. * See COPYING.txt for license details. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Routines for bitblt's. * */ #include #include #include #include "u_mem.h" #include "gr.h" #include "rle.h" #include "dxxerror.h" #include "byteutil.h" #if DXX_USE_OGL #include "ogl_init.h" #endif #include "compiler-range_for.h" #include "d_range.h" #include namespace dcx { namespace { static void gr_bm_ubitblt00_rle(unsigned w, unsigned h, int dx, int dy, int sx, int sy, const grs_bitmap &src, grs_bitmap &dest); #if !DXX_USE_OGL static void gr_bm_ubitblt00m_rle(unsigned w, unsigned h, int dx, int dy, int sx, int sy, const grs_bitmap &src, grs_bitmap &dest); static void gr_bm_ubitblt0x_rle(grs_canvas &dest, unsigned w, unsigned h, int dx, int dy, int sx, int sy, const grs_bitmap &src); #endif #define gr_linear_movsd(S,D,L) memcpy(D,S,L) #if !DXX_USE_OGL static void gr_linear_rep_movsdm(uint8_t *const dest, const uint8_t *const src, const uint_fast32_t num_pixels) { auto predicate = [&](uint8_t s, uint8_t d) { return s == 255 ? d : s; }; std::transform(src, src + num_pixels, dest, dest, predicate); } #endif template static void gr_for_each_bitmap_line(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm, F f) { const size_t src_width = bm.bm_w; const uintptr_t src_rowsize = bm.bm_rowsize; const uintptr_t dest_rowsize = canvas.cv_bitmap.bm_rowsize; auto dest = &(canvas.cv_bitmap.get_bitmap_data()[ dest_rowsize*y+x ]); auto src = bm.get_bitmap_data(); for (uint_fast32_t y1 = bm.bm_h; y1 --;) { f(dest, src, src_width); src += src_rowsize; dest+= dest_rowsize; } } static void gr_ubitmap00(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm) { #if defined(WIN32) && defined(__GNUC__) && (__GNUC__ >= 6 && __GNUC__ <= 10) /* * When using memcpy directly, i686-w64-mingw32-g++-6.3.0 fails to * deduce the template instantiation correctly, leading to a compiler * crash. i686-w64-mingw32-g++-5.4.0 works correctly. Other platforms * work correctly. For the affected cases, define a trivial wrapper, * which gcc deduces correctly. * * This appears to be gcc bug #71740. * * * Known affected: * - i686-w64-mingw32-g++-6.3.0 * - i686-w64-mingw32-g++-7.3.0 * - i686-w64-mingw32-g++-10.2.0 * * Restrict this workaround to known broken versions. */ void *(__attribute__((__cdecl__)) *d_memcpy)(void*, const void*, size_t) = memcpy; #else #define d_memcpy memcpy #endif gr_for_each_bitmap_line(canvas, x, y, bm, d_memcpy); } #if !DXX_USE_OGL static void gr_ubitmap00m(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm) { gr_for_each_bitmap_line(canvas, x, y, bm, gr_linear_rep_movsdm); } #endif template static inline void gr_for_each_bitmap_byte(grs_canvas &canvas, const uint_fast32_t bx, const uint_fast32_t by, const grs_bitmap &bm, F f) { auto src = bm.bm_data; const auto ey = by + bm.bm_h; const auto ex = bx + bm.bm_w; range_for (const auto iy, xrange(by, ey)) range_for (const auto ix, xrange(bx, ex)) f(canvas, src++, ix, iy); } static void gr_ubitmap012(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm) { const auto a = [](grs_canvas &cv, const color_palette_index *const src, const uint_fast32_t px, const uint_fast32_t py) { const auto color = *src; gr_upixel(cv.cv_bitmap, px, py, color); }; gr_for_each_bitmap_byte(canvas, x, y, bm, a); } #if !DXX_USE_OGL static void gr_ubitmap012m(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm) { const auto a = [](grs_canvas &cv, const color_palette_index *const src, const uint_fast32_t px, const uint_fast32_t py) { const uint8_t c = *src; if (c != 255) { gr_upixel(cv.cv_bitmap, px, py, c); } }; gr_for_each_bitmap_byte(canvas, x, y, bm, a); } #endif static void gr_ubitmapGENERIC(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm) { const uint_fast32_t bm_h = bm.bm_h; const uint_fast32_t bm_w = bm.bm_w; range_for (const uint_fast32_t y1, xrange(bm_h)) { range_for (const uint_fast32_t x1, xrange(bm_w)) { const auto color = gr_gpixel(bm, x1, y1); gr_upixel(canvas.cv_bitmap, x + x1, y + y1, color); } } } #if !DXX_USE_OGL static void gr_ubitmapGENERICm(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm) { const uint_fast32_t bm_h = bm.bm_h; const uint_fast32_t bm_w = bm.bm_w; range_for (const uint_fast32_t y1, xrange(bm_h)) { range_for (const uint_fast32_t x1, xrange(bm_w)) { const auto c = gr_gpixel(bm,x1,y1); if (c != TRANSPARENCY_COLOR) { gr_upixel(canvas.cv_bitmap, x + x1, y + y1, c); } } } } #endif } void gr_ubitmap(grs_canvas &canvas, grs_bitmap &bm) { const unsigned x = 0; const unsigned y = 0; const auto source = bm.get_type(); const auto dest = canvas.cv_bitmap.get_type(); if (source==bm_mode::linear) { switch( dest ) { case bm_mode::linear: if (bm.get_flag_mask(BM_FLAG_RLE)) gr_bm_ubitblt00_rle(bm.bm_w, bm.bm_h, x, y, 0, 0, bm, canvas.cv_bitmap); else gr_ubitmap00(canvas, x, y, bm); return; #if DXX_USE_OGL case bm_mode::ogl: ogl_ubitmapm_cs(canvas, x, y, opengl_bitmap_use_dst_canvas, opengl_bitmap_use_dst_canvas, bm, ogl_colors::white); return; #endif default: gr_ubitmap012(canvas, x, y, bm); return; } } else { gr_ubitmapGENERIC(canvas, x, y, bm); } } #if !DXX_USE_OGL void gr_ubitmapm(grs_canvas &canvas, const unsigned x, const unsigned y, grs_bitmap &bm) { const auto source = bm.get_type(); if (source==bm_mode::linear) { switch(canvas.cv_bitmap.get_type()) { case bm_mode::linear: if (bm.get_flag_mask(BM_FLAG_RLE)) gr_bm_ubitblt00m_rle(bm.bm_w, bm.bm_h, x, y, 0, 0, bm, canvas.cv_bitmap); else gr_ubitmap00m(canvas, x, y, bm); return; default: gr_ubitmap012m(canvas, x, y, bm); return; } } else { gr_ubitmapGENERICm(canvas, x, y, bm); } } namespace { // From Linear to Linear static void gr_bm_ubitblt00(const unsigned w, const unsigned h, const unsigned dx, const unsigned dy, const unsigned sx, const unsigned sy, const grs_bitmap &src, grs_bitmap &dest) { //int src_bm_rowsize_2, dest_bm_rowsize_2; auto sbits = &src.get_bitmap_data()[(src.bm_rowsize * sy) + sx]; auto dbits = &dest.get_bitmap_data()[(dest.bm_rowsize * dy) + dx]; const auto dstep = dest.bm_rowsize; // No interlacing, copy the whole buffer. for (uint_fast32_t i = h; i --;) { gr_linear_movsd( sbits, dbits, w ); //memcpy(dbits, sbits, w); sbits += src.bm_rowsize; dbits += dstep; } } // From Linear to Linear Masked static void gr_bm_ubitblt00m(const unsigned w, const uint_fast32_t h, const unsigned dx, const unsigned dy, const unsigned sx, const unsigned sy, const grs_bitmap &src, grs_bitmap &dest) { //int src_bm_rowsize_2, dest_bm_rowsize_2; auto sbits = &src.get_bitmap_data()[(src.bm_rowsize * sy) + sx]; auto dbits = &dest.get_bitmap_data()[(dest.bm_rowsize * dy) + dx]; // No interlacing, copy the whole buffer. { for (auto i = h; i; --i) { gr_linear_rep_movsdm(dbits, sbits, w); sbits += src.bm_rowsize; dbits += dest.bm_rowsize; } } } } void gr_bm_ubitblt(grs_canvas &canvas, const unsigned w, const unsigned h, const int dx, const int dy, const int sx, const int sy, const grs_bitmap &src) { auto &dest = canvas.cv_bitmap; if (src.get_type() == bm_mode::linear && dest.get_type() == bm_mode::linear) { if (src.get_flag_mask(BM_FLAG_RLE)) gr_bm_ubitblt00_rle( w, h, dx, dy, sx, sy, src, dest ); else gr_bm_ubitblt00( w, h, dx, dy, sx, sy, src, dest ); return; } if (src.get_flag_mask(BM_FLAG_RLE) && src.get_type() == bm_mode::linear) { gr_bm_ubitblt0x_rle(canvas, w, h, dx, dy, sx, sy, src); return; } range_for (const uint_fast32_t y1, xrange(h)) range_for (const uint_fast32_t x1, xrange(w)) gr_bm_pixel(canvas, dest, dx + x1, dy + y1, gr_gpixel(src, sx + x1, sy + y1)); } #endif // Clipped bitmap ... void gr_bitmap(grs_canvas &canvas, const unsigned x, const unsigned y, grs_bitmap &bm) { int dx1=x, dx2=x+bm.bm_w-1; int dy1=y, dy2=y+bm.bm_h-1; if (dx1 >= canvas.cv_bitmap.bm_w || dx2 < 0) return; if (dy1 >= canvas.cv_bitmap.bm_h || dy2 < 0) return; // Draw bitmap bm[x,y] into (dx1,dy1)-(dx2,dy2) #if DXX_USE_OGL ogl_ubitmapm_cs(canvas, x, y, opengl_bitmap_use_src_bitmap, opengl_bitmap_use_src_bitmap, bm, ogl_colors::white); #else int sx = 0, sy = 0; if ( dx1 < 0 ) { sx = -dx1; dx1 = 0; } if ( dy1 < 0 ) { sy = -dy1; dy1 = 0; } if (dx2 >= canvas.cv_bitmap.bm_w) dx2 = canvas.cv_bitmap.bm_w - 1; if (dy2 >= canvas.cv_bitmap.bm_h) dy2 = canvas.cv_bitmap.bm_h - 1; gr_bm_ubitblt(canvas, dx2 - dx1 + 1, dy2 - dy1 + 1, dx1, dy1, sx, sy, bm); #endif } #if !DXX_USE_OGL void gr_bitmapm(grs_canvas &canvas, const unsigned x, const unsigned y, const grs_bitmap &bm) { int dx1=x, dx2=x+bm.bm_w-1; int dy1=y, dy2=y+bm.bm_h-1; int sx=0, sy=0; if (dx1 >= canvas.cv_bitmap.bm_w || dx2 < 0) return; if (dy1 >= canvas.cv_bitmap.bm_h || dy2 < 0) return; if ( dx1 < 0 ) { sx = -dx1; dx1 = 0; } if ( dy1 < 0 ) { sy = -dy1; dy1 = 0; } if (dx2 >= canvas.cv_bitmap.bm_w) dx2 = canvas.cv_bitmap.bm_w - 1; if (dy2 >= canvas.cv_bitmap.bm_h) dy2 = canvas.cv_bitmap.bm_h - 1; // Draw bitmap bm[x,y] into (dx1,dy1)-(dx2,dy2) if (bm.get_type() == bm_mode::linear && canvas.cv_bitmap.get_type() == bm_mode::linear) { if (bm.get_flag_mask(BM_FLAG_RLE)) gr_bm_ubitblt00m_rle(dx2 - dx1 + 1, dy2 - dy1 + 1, dx1, dy1, sx, sy, bm, canvas.cv_bitmap ); else gr_bm_ubitblt00m(dx2 - dx1 + 1, dy2 - dy1 + 1, dx1, dy1, sx, sy, bm, canvas.cv_bitmap ); return; } gr_bm_ubitbltm(canvas, dx2 - dx1 + 1, dy2 - dy1 + 1, dx1, dy1, sx, sy, bm); } void gr_bm_ubitbltm(grs_canvas &canvas, const unsigned w, const unsigned h, const unsigned dx, const unsigned dy, const unsigned sx, const unsigned sy, const grs_bitmap &src) { ubyte c; auto &dest = canvas.cv_bitmap; range_for (const uint_fast32_t y1, xrange(h)) range_for (const uint_fast32_t x1, xrange(w)) if ((c=gr_gpixel(src,sx+x1,sy+y1))!=255) gr_bm_pixel(canvas, dest, dx + x1, dy + y1, c); } #endif namespace { class bm_rle_window : bm_rle_src_stride { public: bm_rle_window(const grs_bitmap &src) : bm_rle_src_stride(src, src.get_flag_mask(BM_FLAG_RLE_BIG)) { } void skip_upper_rows(uint_fast32_t); uint8_t *init(uint_fast32_t dx, uint_fast32_t dy, uint_fast32_t sy, grs_bitmap &dest); template void apply(uint_fast32_t w, uint_fast32_t h, uint_fast32_t sx, uint8_t *dbits, uint_fast32_t bm_rowsize, F &&f); #if !DXX_USE_OGL using bm_rle_src_stride::src_bits; using bm_rle_src_stride::advance_src_bits; #endif }; void bm_rle_window::skip_upper_rows(const uint_fast32_t sy) { for (uint_fast32_t i = sy; i; --i) advance_src_bits(); } uint8_t *bm_rle_window::init(const uint_fast32_t dx, const uint_fast32_t dy, const uint_fast32_t sy, grs_bitmap &dest) { skip_upper_rows(sy); return &dest.get_bitmap_data()[(dest.bm_rowsize * dy) + dx]; } template void bm_rle_window::apply(const uint_fast32_t w, const uint_fast32_t h, const uint_fast32_t sx, uint8_t *dbits, const uint_fast32_t bm_rowsize, F &&f) { // No interlacing, copy the whole buffer. for (uint_fast32_t i = h; i; --i) { f(std::exchange(dbits, dbits + bm_rowsize), src_bits, sx, w); advance_src_bits(); } } static void gr_bm_ubitblt00_rle(const unsigned w, const unsigned h, const int dx, const int dy, const int sx, const int sy, const grs_bitmap &src, grs_bitmap &dest) { bm_rle_window bw(src); bw.apply(sx + w - 1, h, sx, bw.init(dx, dy, sy, dest), dest.bm_rowsize, gr_rle_expand_scanline); } #if !DXX_USE_OGL static void gr_bm_ubitblt00m_rle(const unsigned w, const unsigned h, const int dx, const int dy, const int sx, const int sy, const grs_bitmap &src, grs_bitmap &dest) { bm_rle_window bw(src); bw.apply(sx + w - 1, h, sx, bw.init(dx, dy, sy, dest), dest.bm_rowsize, gr_rle_expand_scanline_masked); } // in rle.c static void gr_bm_ubitblt0x_rle(grs_canvas &canvas, const unsigned w, const unsigned h, const int dx, const int dy, const int sx, const int sy, const grs_bitmap &src) { bm_rle_window bw(src); bw.skip_upper_rows(sy); range_for (const uint_fast32_t y1, xrange(h)) { const auto sbits = bw.src_bits; gr_rle_expand_scanline_generic(canvas, canvas.cv_bitmap, dx, dy + y1, sbits, sx, sx + w-1); bw.advance_src_bits(); } } #endif // rescalling bitmaps, 10/14/99 Jan Bobrowski jb@wizard.ae.krakow.pl static void scale_line(const uint8_t *in, uint8_t *out, const uint_fast32_t ilen, const uint_fast32_t olen) { uint_fast32_t a = olen / ilen, b = olen % ilen, c = 0; for (uint8_t *const end = out + olen; out != end;) { uint_fast32_t i = a; c += b; if(c >= ilen) { c -= ilen; ++i; } auto e = out + i; std::fill(std::exchange(out, e), e, *in++); } } static void gr_bitmap_scale_to(const grs_bitmap &src, grs_bitmap &dst) { auto s = src.get_bitmap_data(); auto d = dst.get_bitmap_data(); int h = src.bm_h; int a = dst.bm_h/h, b = dst.bm_h%h; int c = 0, i; for (uint_fast32_t y = src.bm_h; y; --y) { i = a; c += b; if(c >= h) { c -= h; goto inside; } while(--i>=0) { inside: scale_line(s, d, src.bm_w, dst.bm_w); d += dst.bm_rowsize; } s += src.bm_rowsize; } } } void show_fullscr(grs_canvas &canvas, grs_bitmap &bm) { auto &scr = canvas.cv_bitmap; #if DXX_USE_OGL if (bm.get_type() == bm_mode::linear && scr.get_type() == bm_mode::ogl && bm.bm_w <= grd_curscreen->get_screen_width() && bm.bm_h <= grd_curscreen->get_screen_height()) // only scale with OGL if bitmap is not bigger than screen size { ogl_ubitmapm_cs(canvas, 0, 0, opengl_bitmap_use_dst_canvas, opengl_bitmap_use_dst_canvas, bm, ogl_colors::white);//use opengl to scale, faster and saves ram. -MPM return; } #endif if (scr.get_type() != bm_mode::linear) { grs_bitmap_ptr p = gr_create_bitmap(scr.bm_w, scr.bm_h); auto &tmp = *p.get(); gr_bitmap_scale_to(bm, tmp); gr_bitmap(canvas, 0, 0, tmp); return; } gr_bitmap_scale_to(bm, scr); } // Find transparent area in bitmap void gr_bitblt_find_transparent_area(const grs_bitmap &bm, unsigned &minx, unsigned &miny, unsigned &maxx, unsigned &maxy) { using std::advance; using std::min; using std::max; if (!bm.get_flag_mask(BM_FLAG_TRANSPARENT)) return; minx = bm.bm_w - 1; maxx = 0; miny = bm.bm_h - 1; maxy = 0; unsigned i = 0, count = 0; auto check = [&](unsigned x, unsigned y, const color_palette_index c) { if (c == TRANSPARENCY_COLOR) { // don't look for transparancy color here. count++; minx = min(x, minx); miny = min(y, miny); maxx = max(x, maxx); maxy = max(y, maxy); } }; // decode the bitmap const uint_fast32_t bm_h = bm.bm_h; const uint_fast32_t bm_w = bm.bm_w; if (bm.get_flag_mask(BM_FLAG_RLE)) { bm_rle_expand expander(bm); for (uint_fast32_t y = 0;; ++y) { std::array buf; if (expander.step(bm_rle_expand_range(buf)) != bm_rle_expand::again) break; range_for (const uint_fast32_t x, xrange(bm_w)) check(x, y, buf[x]); } } else { range_for (const uint_fast32_t y, xrange(bm_h)) range_for (const uint_fast32_t x, xrange(bm_w)) check(x, y, bm.bm_data[i++]); } Assert (count); } }