diff --git a/LEGO1/mxbitmap.cpp b/LEGO1/mxbitmap.cpp index 8f33fc22..1128fded 100644 --- a/LEGO1/mxbitmap.cpp +++ b/LEGO1/mxbitmap.cpp @@ -369,7 +369,7 @@ MxResult MxBitmap::SetBitDepth(MxBool p_isHighColor) MxResult MxBitmap::StretchBits(HDC p_hdc, MxS32 p_xSrc, MxS32 p_ySrc, MxS32 p_xDest, MxS32 p_yDest, MxS32 p_destWidth, MxS32 p_destHeight) { // Compression fix? - if ((this->m_bmiHeader->biCompression != 16) && (0 < this->m_bmiHeader->biHeight)) { + if ((this->m_bmiHeader->biCompression != BI_RGB_TOPDOWN) && (0 < this->m_bmiHeader->biHeight)) { p_ySrc = (this->m_bmiHeader->biHeight - p_destHeight) - p_ySrc; } diff --git a/LEGO1/mxbitmap.h b/LEGO1/mxbitmap.h index 53b01e16..6bb1c5fd 100644 --- a/LEGO1/mxbitmap.h +++ b/LEGO1/mxbitmap.h @@ -21,6 +21,13 @@ struct MxBITMAPINFO { RGBQUAD bmiColors[256]; }; +// Non-standard value for biCompression in the BITMAPINFOHEADER struct. +// By default, uncompressed bitmaps (BI_RGB) are stored in bottom-up order. +// You can specify that the bitmap has top-down order instead by providing +// a negative number for biHeight. It could be that Mindscape decided on a +// belt & suspenders approach here. +#define BI_RGB_TOPDOWN 0x10 + // SIZE 0x20 // VTABLE 0x100dc7b0 class MxBitmap : public MxCore @@ -43,6 +50,10 @@ public: virtual MxResult StretchBits(HDC p_hdc, MxS32 p_xSrc, MxS32 p_ySrc, MxS32 p_xDest, MxS32 p_yDest, MxS32 p_destWidth, MxS32 p_destHeight); // vtable+40 inline BITMAPINFOHEADER *GetBmiHeader() const { return m_bmiHeader; } + inline MxLong GetBmiWidth() const { return m_bmiHeader->biWidth; } + inline MxLong GetBmiHeight() const { return m_bmiHeader->biHeight; } + inline MxLong GetBmiHeightAbs() const { return m_bmiHeader->biHeight > 0 ? m_bmiHeader->biHeight : -m_bmiHeader->biHeight; } + inline MxU8 *GetBitmapData() const { return m_data; } private: MxResult ImportColorsToPalette(RGBQUAD*, MxPalette*); diff --git a/LEGO1/mxvideopresenter.cpp b/LEGO1/mxvideopresenter.cpp index a22adcd4..5abef00d 100644 --- a/LEGO1/mxvideopresenter.cpp +++ b/LEGO1/mxvideopresenter.cpp @@ -2,7 +2,7 @@ #include "MxVideoManager.h" DECOMP_SIZE_ASSERT(MxVideoPresenter, 0x64); -DECOMP_SIZE_ASSERT(MxVideoPresenter::UnkStruct, 0xc); +DECOMP_SIZE_ASSERT(MxVideoPresenter::AlphaMask, 0xc); // OFFSET: LEGO1 0x1000c700 void MxVideoPresenter::VTable0x5c(undefined4 p_unknown1) @@ -49,28 +49,116 @@ LPDIRECTDRAWSURFACE MxVideoPresenter::VTable0x78() // OFFSET: LEGO1 0x1000c7c0 MxBool MxVideoPresenter::VTable0x7c() { - return (m_bitmap != NULL) || (m_unk54 != NULL); + return (m_bitmap != NULL) || (m_alpha != NULL); } // OFFSET: LEGO1 0x1000c7e0 MxS32 MxVideoPresenter::GetWidth() { - return m_unk54 ? m_unk54->width + return m_alpha ? m_alpha->m_width : m_bitmap->GetBmiHeader()->biWidth; } // OFFSET: LEGO1 0x1000c800 MxS32 MxVideoPresenter::GetHeight() { - return m_unk54 ? m_unk54->height + return m_alpha ? m_alpha->m_height : m_bitmap->GetBmiHeader()->biHeight; } +// OFFSET: LEGO1 0x100b24f0 +MxVideoPresenter::AlphaMask::AlphaMask(MxBitmap &p_bitmap) +{ + m_width = p_bitmap.GetBmiWidth(); + // DECOMP: ECX becomes word-sized if these are not two separate actions. + MxLong _height = p_bitmap.GetBmiHeightAbs(); + m_height = _height; + + MxS32 size = ((m_width * m_height) / 8) + 1; + m_bitmask = new MxU8[size]; + memset(m_bitmask, 0, size); + + MxU32 biCompression = p_bitmap.GetBmiHeader()->biCompression; + MxU32 rows_before_top; + MxU8 *bitmap_src_ptr; + + // The goal here is to enable us to walk through the bitmap's rows + // in order, regardless of the orientation. We want to end up at the + // start of the first row, which is either at position 0, or at + // (image_stride * biHeight) - 1. + + // Reminder: Negative biHeight means this is a top-down DIB. + // Otherwise it is bottom-up. + + if (biCompression == BI_RGB) { + // DECOMP: I think this must be an OR. If not, the check for + // biCompression == 16 gets optimized away. + if (biCompression == BI_RGB_TOPDOWN || p_bitmap.GetBmiHeight() < 0) { + rows_before_top = 0; + } else { + rows_before_top = p_bitmap.GetBmiHeightAbs(); + rows_before_top--; + } + + goto seek_to_last_row; + } else if (biCompression == BI_RGB_TOPDOWN) { + // DECOMP: This is the only condition where we skip the + // calculation below. + bitmap_src_ptr = p_bitmap.GetBitmapData(); + } else { + if (p_bitmap.GetBmiHeight() < 0) { + rows_before_top = 0; + } else { + rows_before_top = p_bitmap.GetBmiHeightAbs(); + rows_before_top--; + } + +// TODO: would prefer not to use goto if we can figure this structure out +seek_to_last_row: + bitmap_src_ptr = ((p_bitmap.GetBmiWidth()+3)&-4) * rows_before_top + p_bitmap.GetBitmapData(); + } + + // How many bytes are there for each row of the bitmap? + // (i.e. the image stride) + // If this is a bottom-up DIB, we will walk it in reverse. + // TODO: Same rounding trick as in MxBitmap + MxS32 row_seek = ((m_height+3)&-4); + if (p_bitmap.GetBmiHeight() < 0) + row_seek = -row_seek; + + // The actual offset into the m_bitmask array. The two for-loops + // are just for counting the pixels. + MxS32 offset = 0; + + MxU8 *t_ptr = bitmap_src_ptr; + for (MxS32 j = 0; j < m_height; j++) { + for (MxS32 i = 0; i < m_width; i++) { + if (*t_ptr) { + // TODO: Second CDQ instruction for abs() should not be there. + MxU32 shift = abs(offset) & 7; + m_bitmask[offset / 8] |= (1 << abs(shift)); + } + t_ptr++; + offset++; + } + // Seek to the start of the next row + bitmap_src_ptr += row_seek; + t_ptr = bitmap_src_ptr; + } +} + +// OFFSET: LEGO1 0x100b26d0 +MxVideoPresenter::AlphaMask::~AlphaMask() +{ + if (m_bitmask) + delete[] m_bitmask; +} + // OFFSET: LEGO1 0x100b2760 void MxVideoPresenter::Init() { m_bitmap = NULL; - m_unk54 = NULL; + m_alpha = NULL; m_unk5c = 1; m_unk58 = NULL; m_unk60 = -1; @@ -99,7 +187,7 @@ void MxVideoPresenter::Destroy(MxBool p_fromDestructor) m_flags = m_flags & 0xfb; } - if (MVideoManager() && (m_unk54 || m_bitmap)) { + if (MVideoManager() && (m_alpha || m_bitmap)) { MxS32 height = GetHeight(); MxS32 width = GetWidth(); @@ -112,7 +200,7 @@ void MxVideoPresenter::Destroy(MxBool p_fromDestructor) } delete m_bitmap; - delete m_unk54; + delete m_alpha; Init(); diff --git a/LEGO1/mxvideopresenter.h b/LEGO1/mxvideopresenter.h index 30a6ada7..4f66075c 100644 --- a/LEGO1/mxvideopresenter.h +++ b/LEGO1/mxvideopresenter.h @@ -49,16 +49,18 @@ public: // TODO: Not sure what this is. Seems to have size of 12 bytes // based on 0x100b9e9a. Values are copied from the bitmap header. - struct UnkStruct { - undefined unk0[4]; - MxU16 width; - MxU16 height; + // SIZE 0xc + struct AlphaMask { + MxU8 *m_bitmask; + MxU16 m_width; + MxU16 m_height; - virtual ~UnkStruct() {} + AlphaMask(MxBitmap &); + virtual ~AlphaMask(); }; MxBitmap *m_bitmap; - UnkStruct *m_unk54; + AlphaMask *m_alpha; LPDIRECTDRAWSURFACE m_unk58; undefined2 m_unk5c; unsigned char m_flags; // 0x5e