diff --git a/include/wx/msw/dib.h b/include/wx/msw/dib.h index 2f09804ee2..a3b8cb98e9 100644 --- a/include/wx/msw/dib.h +++ b/include/wx/msw/dib.h @@ -41,8 +41,8 @@ public: #ifdef __WXMSW__ // create a DIB from the DDB - wxDIB(const wxBitmap& bmp) - { Init(); (void)Create(bmp); } + wxDIB(const wxBitmap& bmp, int depth = -1) + { Init(); (void)Create(bmp, depth); } #endif // __WXMSW__ // create a DIB from the Windows DDB @@ -58,9 +58,9 @@ public: // same as the corresponding ctors but with return value bool Create(int width, int height, int depth); #ifdef __WXMSW__ - bool Create(const wxBitmap& bmp) { return Create(GetHbitmapOf(bmp)); } + bool Create(const wxBitmap& bmp, int depth = -1) { return Create(GetHbitmapOf(bmp), depth); } #endif - bool Create(HBITMAP hbmp); + bool Create(HBITMAP hbmp, int depth = -1); bool Load(const wxString& filename); // dtor is not virtual, this class is not meant to be used polymorphically @@ -154,14 +154,14 @@ public: // can be used with ::AlphaBlend() but it is also possible to disable // pre-multiplication for the DIB to be usable with ImageList_Draw() which // does pre-multiplication internally. - wxDIB(const wxImage& image, PixelFormat pf = PixelFormat_PreMultiplied) + wxDIB(const wxImage& image, PixelFormat pf = PixelFormat_PreMultiplied, int depth = -1) { Init(); - (void)Create(image, pf); + (void)Create(image, pf, depth); } // same as the above ctor but with the return code - bool Create(const wxImage& image, PixelFormat pf = PixelFormat_PreMultiplied); + bool Create(const wxImage& image, PixelFormat pf = PixelFormat_PreMultiplied, int depth = -1); // create wxImage having the same data as this DIB diff --git a/include/wx/rawbmp.h b/include/wx/rawbmp.h index 12eb283723..c32122d5b9 100644 --- a/include/wx/rawbmp.h +++ b/include/wx/rawbmp.h @@ -153,6 +153,24 @@ typedef wxPixelFormat wxImagePixelFormat; typedef wxPixelFormat wxNativePixelFormat; #define wxPIXEL_FORMAT_ALPHA 3 + + template<> + struct wxPixelFormat + { + // the type which may hold the entire pixel value + typedef bool PixelType; + + // size of one pixel in bits + static const int BitsPerPixel = 1; + + // size of one pixel in ChannelType units (usually bytes) + static const int SizePixel = 1; + + // true if we have an alpha channel (together with the other channels, this + // doesn't cover the case of wxImage which stores alpha separately) + enum { HasAlpha = false }; + }; + typedef wxPixelFormat wxMonoPixelFormat; #elif defined(__WXMAC__) // under Mac, first component is unused but still present, hence we use // 32bpp, not 24 @@ -682,6 +700,220 @@ struct wxPixelDataOut }; }; + #if defined(__WXMSW__) + template <> + struct wxPixelDataOut::wxPixelDataIn : public wxPixelDataBase + { + public: + // the type of the class we're working with + typedef wxBitmap ImageType; + + // Reference emulates ChannelType& for monochrome bitmap + class Iterator; + class Reference + { + public: + Reference& operator=(bool b) + { + wxByte mask = static_cast(1 << m_bit); + wxByte value = static_cast(b << m_bit); + (*m_ptr &= ~mask) |= value; + return *this; + } + operator bool() const + { + wxByte mask = static_cast(1 << m_bit); + return (*m_ptr & mask) != 0; + } + + private: + Reference(const Iterator& i) : + m_ptr(i.m_ptr), + m_bit(i.m_bit) + { + } + + wxByte* m_ptr; + wxInt8 m_bit; + friend class Iterator; + }; + + class Iterator + { + public: + // emulate unspecialized template + typedef wxMonoPixelFormat Format; + + // the pixel format we use + typedef Format PixelFormat; + + // the pixel data we're working with + typedef wxPixelDataOut::wxPixelDataIn PixelData; + + + // go back to (0, 0) + void Reset(const PixelData& data) + { + *this = data.GetPixels(); + } + + // initializes the iterator to point to the origin of the given + // pixel data + Iterator(PixelData& data) + { + Reset(data); + } + + // initializes the iterator to point to the origin of the given + // bitmap + Iterator(wxBitmap& bmp, PixelData& data) + { + // using cast here is ugly but it should be safe as + // GetRawData() real return type should be consistent with + // BitsPerPixel (which is in turn defined by ChannelType) and + // this is the only thing we can do without making GetRawData() + // a template function which is undesirable + m_ptr = (wxByte*) + bmp.GetRawData(data, PixelFormat::BitsPerPixel); + m_bit = 7; + } + + // default constructor + Iterator() + { + m_ptr = NULL; + // m_bit doesn't need to be set until m_ptr != NULL + } + + // return true if this iterator is valid + bool IsOk() const { return m_ptr != NULL; } + + + // navigation + // ---------- + + // advance the iterator to the next pixel, prefix version + Iterator& operator++() + { + --m_bit; + m_ptr += (m_bit < 0); + m_bit &= 0x7; + + return *this; + } + + // postfix (hence less efficient -- don't use it unless you + // absolutely must) version + Iterator operator++(int) + { + Iterator p(*this); + ++* this; + return p; + } + + // move x pixels to the right and y down + // + // note that the rows don't wrap! + void Offset(const PixelData& data, int x, int y) + { + m_ptr += data.GetRowStride() * y; + x += 7 - m_bit; + m_ptr += x >> 3; + m_bit = 7 - (x & 0x7); + } + + // move x pixels to the right (again, no row wrapping) + void OffsetX(const PixelData& WXUNUSED(data), int x) + { + x += 7 - m_bit; + m_ptr += x >> 3; + m_bit = 7 - (x & 0x7); + } + + // move y rows to the bottom + void OffsetY(const PixelData& data, int y) + { + m_ptr += data.GetRowStride() * y; + } + + // go to the given position + void MoveTo(const PixelData& data, int x, int y) + { + Reset(data); + Offset(data, x, y); + } + + + // data access + // ----------- + + // access to individual pixels + Reference Pixel() { return Reference(*this); } + + // private: -- see comment in the beginning of the file + + // I don't see a way to bit-twiddle without two fields + wxByte* m_ptr; + wxInt8 m_bit; + }; + + // ctor associates this pointer with a bitmap and locks the bitmap for + // raw access, it will be unlocked only by our dtor and so these + // objects should normally be only created on the stack, i.e. have + // limited life-time + wxPixelDataIn(wxBitmap& bmp) : m_bmp(bmp), m_pixels(bmp, *this) + { + } + + wxPixelDataIn(wxBitmap& bmp, const wxRect& rect) + : m_bmp(bmp), m_pixels(bmp, *this) + { + InitRect(rect.GetPosition(), rect.GetSize()); + } + + wxPixelDataIn(wxBitmap& bmp, const wxPoint& pt, const wxSize& sz) + : m_bmp(bmp), m_pixels(bmp, *this) + { + InitRect(pt, sz); + } + + // we evaluate to true only if we could get access to bitmap data + // successfully + operator bool() const { return m_pixels.IsOk(); } + + // get the iterator pointing to the origin + Iterator GetPixels() const { return m_pixels; } + + // dtor unlocks the bitmap + ~wxPixelDataIn() + { + if ( m_pixels.IsOk() ) + { + m_bmp.UngetRawData(*this); + } + // else: don't call UngetRawData() if GetRawData() failed + } + + // private: -- see comment in the beginning of the file + + // the bitmap we're associated with + wxBitmap m_bmp; + + // the iterator pointing to the image origin + Iterator m_pixels; + + private: + void InitRect(const wxPoint& pt, const wxSize& sz) + { + m_pixels.Offset(*this, pt.x, pt.y); + + m_ptOrigin = pt; + m_width = sz.x; + m_height = sz.y; + } + }; + #endif + #endif //wxUSE_GUI template wxImagePixelData; typedef wxPixelData wxNativePixelData; typedef wxPixelData wxAlphaPixelData; +#if defined(__WXMSW__) +typedef wxPixelData wxMonoPixelData; +#endif + #endif //wxUSE_GUI // ---------------------------------------------------------------------------- diff --git a/src/msw/bitmap.cpp b/src/msw/bitmap.cpp index 13454df1e3..cb6b03a40d 100644 --- a/src/msw/bitmap.cpp +++ b/src/msw/bitmap.cpp @@ -70,7 +70,7 @@ public: #if wxUSE_WXDIB // Creates a new bitmap (DDB or DIB) from the contents of the given DIB. - void CopyFromDIB(const wxDIB& dib); + void CopyFromDIB(const wxDIB& dib, int depth = -1); // Takes ownership of the given DIB. bool AssignDIB(wxDIB& dib); @@ -318,14 +318,15 @@ void wxBitmapRefData::InitFromDIB(const wxDIB& dib, HBITMAP hbitmap) m_hBitmap = (WXHBITMAP)hbitmap; } -void wxBitmapRefData::CopyFromDIB(const wxDIB& dib) +void wxBitmapRefData::CopyFromDIB(const wxDIB& dib, int depth /* = -1 */) { wxCHECK_RET( !IsOk(), "bitmap already initialized" ); wxCHECK_RET( dib.IsOk(), wxT("invalid DIB in CopyFromDIB") ); HBITMAP hbitmap; #ifdef SOMETIMES_USE_DIB - hbitmap = dib.CreateDDB(); + // MemoryHDC defaults to monochrome + hbitmap = dib.CreateDDB(depth == 1 ? HDC(MemoryHDC()) : NULL); #else // ALWAYS_USE_DIB hbitmap = NULL; #endif // SOMETIMES_USE_DIB/ALWAYS_USE_DIB @@ -845,7 +846,7 @@ bool wxBitmap::CreateFromImage(const wxImage& image, int depth, WXHDC hdc) const int h = image.GetHeight(); const int w = image.GetWidth(); - wxDIB dib(image); + wxDIB dib(image, wxDIB::PixelFormat_PreMultiplied, depth); if ( !dib.IsOk() ) return false; @@ -1269,7 +1270,7 @@ void wxBitmap::MSWBlendMaskWithAlpha() { wxBitmap bmpMask = GetMask()->GetBitmap(); - wxNativePixelData maskData(bmpMask); + wxMonoPixelData maskData(bmpMask); wxCHECK_RET(maskData, "No access to bitmap mask data"); wxAlphaPixelData bmpData(*this); @@ -1278,17 +1279,17 @@ void wxBitmap::MSWBlendMaskWithAlpha() const int w = GetWidth(); const int h = GetHeight(); - wxNativePixelData::Iterator maskRowStart(maskData); + wxMonoPixelData::Iterator maskRowStart(maskData); wxAlphaPixelData::Iterator bmpRowStart(bmpData); for ( int y = 0; y < h; y++ ) { - wxNativePixelData::Iterator pMask = maskRowStart; + wxMonoPixelData::Iterator pMask = maskRowStart; wxAlphaPixelData::Iterator pBmp = bmpRowStart; for ( int x = 0; x < w; x++, ++pBmp, ++pMask ) { // Masked pixel is not drawn i.e. is transparent, // non-masked pixel is untouched. - if ( pMask.Red() == 0 ) + if ( pMask.Pixel() == 0 ) { pBmp.Red() = pBmp.Green() = pBmp.Blue() = 0; // pre-multiplied pBmp.Alpha() = wxALPHA_TRANSPARENT; @@ -1378,6 +1379,12 @@ void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp) // no bitmap, no data (raw or otherwise) return NULL; } + if ( bpp == 1 && GetDepth() != 1 ) + { + wxFAIL_MSG( wxT("use wxQuantize if you want to convert color wxBitmap to mono") ); + + return NULL; + } // if we're already a DIB we can access our data directly, but if not we // need to convert this DDB to a DIB section and use it for raw access and @@ -1388,7 +1395,7 @@ void *wxBitmap::GetRawData(wxPixelDataBase& data, int bpp) wxCHECK_MSG( !GetBitmapData()->m_dib, NULL, wxT("GetRawData() may be called only once") ); - wxDIB *dib = new wxDIB(*this); + wxDIB *dib = new wxDIB(*this, bpp); if ( !dib->IsOk() ) { delete dib; @@ -1462,7 +1469,8 @@ void wxBitmap::UngetRawData(wxPixelDataBase& WXUNUSED(data)) GetBitmapData()->m_dib = NULL; GetBitmapData()->Free(); - GetBitmapData()->CopyFromDIB(*dib); + int depth = GetDepth() == 1 && dib->GetDepth() != GetDepth() ? 1 : -1; + GetBitmapData()->CopyFromDIB(*dib, depth); delete dib; } diff --git a/src/msw/dib.cpp b/src/msw/dib.cpp index 3e0498b249..c52d733867 100644 --- a/src/msw/dib.cpp +++ b/src/msw/dib.cpp @@ -37,6 +37,8 @@ #endif //WX_PRECOMP #include "wx/file.h" +#include "wx/quantize.h" +#include "wx/scopedarray.h" #include #include @@ -49,9 +51,12 @@ // private functions // ---------------------------------------------------------------------------- +namespace +{ + // calculate the number of palette entries needed for the bitmap with this // number of bits per pixel -static inline WORD GetNumberOfColours(WORD bitsPerPixel) +inline WORD GetNumberOfColours(WORD bitsPerPixel) { // only 1, 4 and 8bpp bitmaps use palettes (well, they could be used with // 24bpp ones too but we don't support this as I think it's quite uncommon) @@ -59,7 +64,7 @@ static inline WORD GetNumberOfColours(WORD bitsPerPixel) } // wrapper around ::GetObject() for DIB sections -static inline bool GetDIBSection(HBITMAP hbmp, DIBSECTION *ds) +inline bool GetDIBSection(HBITMAP hbmp, DIBSECTION *ds) { // note that GetObject() may return sizeof(DIBSECTION) for a bitmap // which is *not* a DIB section and the way to check for it is @@ -68,6 +73,25 @@ static inline bool GetDIBSection(HBITMAP hbmp, DIBSECTION *ds) ds->dsBm.bmBits; } +// for monochrome bitmaps, need bit twiddling functions to get at pixels +inline bool MonochromeLineReadBit(const unsigned char* srcLineStart, int index) +{ + const unsigned char* byte = srcLineStart + (index >> 3); + int bit = 7 - (index & 7); + unsigned char mask = 1 << bit; + return (*byte & mask) != 0; +} + +inline void MonochromeLineWriteBit(unsigned char* dstLineStart, int index, bool value) +{ + unsigned char* byte = dstLineStart + (index >> 3); + int bit = 7 - (index & 7); + unsigned char mask = ~(1 << bit); + unsigned char newValue = value << bit; + (*byte &= mask) |= newValue; +} + +} // ============================================================================ // implementation // ============================================================================ @@ -81,7 +105,7 @@ bool wxDIB::Create(int width, int height, int depth) // we don't support formats using palettes right now so we only create // either 24bpp (RGB) or 32bpp (RGBA) bitmaps wxASSERT_MSG( depth, wxT("invalid image depth in wxDIB::Create()") ); - if ( depth < 24 ) + if ( depth != 1 && depth < 24 ) depth = 24; // allocate memory for bitmap structures @@ -126,7 +150,7 @@ bool wxDIB::Create(int width, int height, int depth) return true; } -bool wxDIB::Create(HBITMAP hbmp) +bool wxDIB::Create(HBITMAP hbmp, int depth /* = -1 */) { wxCHECK_MSG( hbmp, false, wxT("wxDIB::Create(): invalid bitmap") ); @@ -158,7 +182,7 @@ bool wxDIB::Create(HBITMAP hbmp) return false; } - int d = bm.bmBitsPixel; + int d = depth >= 1 ? depth : bm.bmBitsPixel; if ( d <= 0 ) d = wxDisplayDepth(); @@ -246,15 +270,37 @@ bool wxDIB::Save(const wxString& filename) const size_t sizeHdr = ds.dsBmih.biSize; const size_t sizeImage = ds.dsBmih.biSizeImage; + // provide extra space so we can verify that + // monochrome DIB's color table is size 2 + RGBQUAD monoBmiColors[3]; + UINT nColors = 0; + if ( ds.dsBmih.biBitCount == 1 ) + { + MemoryHDC hDC; + SelectInHDC sDC(hDC, m_handle); + nColors = GetDIBColorTable(hDC, 0, WXSIZEOF(monoBmiColors), monoBmiColors); + if ( nColors != 2 ) + { + wxLogLastError(wxT("GetDIBColorTable")); + return false; + } + } + const size_t colorTableSize = ds.dsBmih.biBitCount == 1 + ? nColors * sizeof(monoBmiColors[0]) + : 0; + bmpHdr.bfType = 0x4d42; // 'BM' in little endian - bmpHdr.bfOffBits = sizeof(BITMAPFILEHEADER) + ds.dsBmih.biSize; + bmpHdr.bfOffBits = sizeof(BITMAPFILEHEADER); + bmpHdr.bfOffBits += ds.dsBmih.biSize; + bmpHdr.bfOffBits += colorTableSize; bmpHdr.bfSize = bmpHdr.bfOffBits + sizeImage; // first write the file header, then the bitmap header and finally the // bitmap data itself ok = file.Write(&bmpHdr, sizeof(bmpHdr)) == sizeof(bmpHdr) && file.Write(&ds.dsBmih, sizeHdr) == sizeHdr && - file.Write(ds.dsBm.bmBits, sizeImage) == sizeImage; + (!colorTableSize || file.Write(monoBmiColors, colorTableSize)) && + file.Write(ds.dsBm.bmBits, sizeImage) == sizeImage; } } #else // !wxUSE_FILE @@ -391,8 +437,11 @@ HBITMAP wxDIB::ConvertToBitmap(const BITMAPINFO *pbmi, HDC hdc, const void *bits HBITMAP hbmp = ::CreateDIBitmap ( - hdc ? hdc // create bitmap compatible - : (HDC) ScreenHDC(), // with this DC + hdc + ? hdc // create bitmap compatible + : pbmih->biBitCount == 1 + ? (HDC) MemoryHDC() + : (HDC) ScreenHDC(), // with this DC pbmih, // used to get size &c CBM_INIT, // initialize bitmap bits too bits, // ... using this data @@ -586,7 +635,7 @@ wxPalette *wxDIB::CreatePalette() const #if wxUSE_IMAGE -bool wxDIB::Create(const wxImage& image, PixelFormat pf) +bool wxDIB::Create(const wxImage& image, PixelFormat pf, int dstDepth) { wxCHECK_MSG( image.IsOk(), false, wxT("invalid wxImage in wxDIB ctor") ); @@ -595,17 +644,64 @@ bool wxDIB::Create(const wxImage& image, PixelFormat pf) // if we have alpha channel, we need to create a 32bpp RGBA DIB, otherwise // a 24bpp RGB is sufficient + // but use monochrome if requested (to support wxMask) const bool hasAlpha = image.HasAlpha(); - const int bpp = hasAlpha ? 32 : 24; + wxCHECK_MSG(!hasAlpha || dstDepth != 1, false, "alpha not supported in monochrome bitmaps"); + const int srcBpp = hasAlpha ? 32 : 24; + dstDepth = dstDepth != -1 ? dstDepth : srcBpp; - if ( !Create(w, h, bpp) ) + if ( !Create(w, h, dstDepth) ) return false; + // if requested, convert wxImage's content to monochrome + wxScopedArray eightBitData; + if ( dstDepth == 1 ) + { + wxImage quantized; + wxPalette* tempPalette; + unsigned char* tempEightBitData; + if ( !wxQuantize::Quantize( + image, + quantized, + &tempPalette, + 2, + &tempEightBitData, + wxQUANTIZE_RETURN_8BIT_DATA) ) + { + return false; + } + wxScopedPtr palette(tempPalette); + eightBitData.reset(tempEightBitData); + + // use palette's colors in result bitmap + MemoryHDC hDC; + SelectInHDC sDC(hDC, m_handle); + RGBQUAD colorTable[2]; + for ( UINT i = 0; i < WXSIZEOF(colorTable); ++i ) + { + if ( !palette->GetRGB(i, + &colorTable[i].rgbRed, + &colorTable[i].rgbGreen, + &colorTable[i].rgbBlue) ) + { + return false; + } + colorTable[i].rgbReserved = 0; + } + UINT rc = SetDIBColorTable(hDC, 0, WXSIZEOF(colorTable), colorTable); + if ( rc != WXSIZEOF(colorTable)) + { + wxLogLastError(wxT("SetDIBColorTable")); + return false; + } + } + // DIBs are stored in bottom to top order (see also the comment above in // Create()) so we need to copy bits line by line and starting from the end - const int srcBytesPerLine = w * 3; - const int dstBytesPerLine = GetLineSize(w, bpp); - const unsigned char *src = image.GetData() + ((h - 1) * srcBytesPerLine); + // N.B.: srcBytesPerLine varies with dstDepth because dstDepth == 1 uses quantized input + const int srcBytesPerLine = dstDepth != 1 ? w * 3 : w; + const int dstBytesPerLine = GetLineSize(w, dstDepth); + const unsigned char *src = (dstDepth != 1 ? image.GetData() : eightBitData.get()) + ((h - 1) * srcBytesPerLine); const unsigned char *alpha = hasAlpha ? image.GetAlpha() + (h - 1)*w : NULL; unsigned char *dstLineStart = (unsigned char *)m_data; @@ -651,12 +747,23 @@ bool wxDIB::Create(const wxImage& image, PixelFormat pf) } else // no alpha channel { - for ( int x = 0; x < w; x++ ) + if ( dstDepth != 1 ) { - *dst++ = src[2]; - *dst++ = src[1]; - *dst++ = src[0]; - src += 3; + for ( int x = 0; x < w; x++ ) + { + *dst++ = src[2]; + *dst++ = src[1]; + *dst++ = src[0]; + src += 3; + } + } + else + { + for ( int x = 0; x < w; x++ ) + { + MonochromeLineWriteBit(dstLineStart, x, src[0] != 0); + ++src; + } } } @@ -716,48 +823,65 @@ wxImage wxDIB::ConvertToImage(ConversionFlags flags) const { // copy one DIB line const unsigned char *src = srcLineStart; - for ( int x = 0; x < w; x++ ) + if ( bpp != 1 ) { - dst[2] = *src++; - dst[1] = *src++; - dst[0] = *src++; - - if ( bpp == 32 ) + for ( int x = 0; x < w; x++ ) { - // wxImage uses non premultiplied alpha so undo - // premultiplication done in Create() above - const unsigned char a = *src; - *alpha++ = a; + dst[2] = *src++; + dst[1] = *src++; + dst[0] = *src++; - // Check what kind of alpha do we have. - switch ( a ) + if ( bpp == 32 ) { - case 0: - hasTransparent = true; - break; + // wxImage uses non premultiplied alpha so undo + // premultiplication done in Create() above + const unsigned char a = *src; + *alpha++ = a; - default: - // Anything in between means we have real transparency - // and must use alpha channel. - hasAlpha = true; - break; + // Check what kind of alpha do we have. + switch ( a ) + { + case 0: + hasTransparent = true; + break; - case 255: - hasOpaque = true; - break; + default: + // Anything in between means we have real transparency + // and must use alpha channel. + hasAlpha = true; + break; + + case 255: + hasOpaque = true; + break; + } + + if ( a > 0 ) + { + dst[0] = (dst[0] * 255) / a; + dst[1] = (dst[1] * 255) / a; + dst[2] = (dst[2] * 255) / a; + } + + src++; } - if ( a > 0 ) - { - dst[0] = (dst[0] * 255) / a; - dst[1] = (dst[1] * 255) / a; - dst[2] = (dst[2] * 255) / a; - } - - src++; + dst += 3; } + } + else + { + for ( int x = 0; x < w; x++ ) + { + unsigned char value = MonochromeLineReadBit(srcLineStart, x) + ? 255 + : 0; + dst[2] = value; + dst[1] = value; + dst[0] = value; - dst += 3; + dst += 3; + } } // pass to the previous line in the image diff --git a/tests/graphics/bitmap.cpp b/tests/graphics/bitmap.cpp index 203fc0e174..c9094ef347 100644 --- a/tests/graphics/bitmap.cpp +++ b/tests/graphics/bitmap.cpp @@ -22,6 +22,7 @@ #include "wx/graphics.h" #endif // wxUSE_GRAPHICS_CONTEXT +#include "testfile.h" #include "testimage.h" #define ASSERT_EQUAL_RGB(c, r, g, b) \ @@ -54,6 +55,52 @@ typedef wxNativePixelData wxNative32PixelData; // tests // ---------------------------------------------------------------------------- +TEST_CASE("BitmapTestCase::Monochrome", "[bitmap][monochrome]") +{ +#ifdef __WXGTK__ + WARN("Skipping test known not to work in wxGTK."); +#else + wxBitmap color; + color.LoadFile("horse.bmp", wxBITMAP_TYPE_BMP); + REQUIRE(color.IsOk()); + REQUIRE(color.GetDepth() == 32); + + wxImage imgQuant = color.ConvertToImage(); + wxBitmap bmpQuant(imgQuant, 1); + REQUIRE(bmpQuant.GetDepth() == 1); + TempFile mono_horse("mono_horse.bmp"); + REQUIRE(bmpQuant.SaveFile(mono_horse.GetName(), wxBITMAP_TYPE_BMP)); + + wxBitmap mono; + REQUIRE(mono.LoadFile(mono_horse.GetName(), wxBITMAP_TYPE_BMP)); + REQUIRE(mono.IsOk()); + REQUIRE(mono.GetDepth() == 1); + + // wxMonoPixelData only exists in wxMSW +#if defined(__WXMSW__) + // draw lines on top and left, but leaving blank top and left lines + { + wxMonoPixelData data(mono); + wxMonoPixelData::Iterator p(data); + p.OffsetY(data, 1); + for ( int i = 0; i < data.GetWidth() - 2; ++i ) + { + ++p; + p.Pixel() = 0; + } + p.MoveTo(data, 1, 1); + for ( int i = 0; i < data.GetHeight() - 3; ++i ) + { + p.OffsetY(data, 1); + p.Pixel() = 1; + } + } + TempFile mono_lines_horse("mono_lines_horse.bmp"); + REQUIRE(mono.SaveFile(mono_lines_horse.GetName(), wxBITMAP_TYPE_BMP)); +#endif // __WXMSW__ +#endif // !__WXGTK__ +} + TEST_CASE("BitmapTestCase::Mask", "[bitmap][mask]") { wxBitmap bmp(10, 10); @@ -726,6 +773,7 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona wxColour maskClrBottomRight; // Fetch sample original mask pixels { + REQUIRE(bmpMask.GetDepth() == 1); wxNativePixelData data(bmpMask); REQUIRE(data); wxNativePixelData::Iterator p(data); @@ -742,11 +790,44 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona p.OffsetX(data, w / 2); // bottom-right point maskClrBottomRight = wxColour(p.Red(), p.Green(), p.Blue()); } + REQUIRE(bmpMask.GetDepth() == 1); CHECK(maskClrTopLeft == *wxWHITE); CHECK(maskClrTopRight == *wxWHITE); CHECK(maskClrBottomLeft == *wxBLACK); CHECK(maskClrBottomRight == *wxBLACK); + // wxMonoPixelData only exists in wxMSW +#if defined(__WXMSW__) + bool maskValueTopLeft; + bool maskValueTopRight; + bool maskValueBottomLeft; + bool maskValueBottomRight; + // Fetch sample original mask pixels + { + REQUIRE(bmpMask.GetDepth() == 1); + wxMonoPixelData data(bmpMask); + REQUIRE(data); + wxMonoPixelData::Iterator p(data); + p.OffsetY(data, h / 4); + wxMonoPixelData::Iterator rowStart = p; + p.OffsetX(data, w / 4); // top-left point + maskValueTopLeft = p.Pixel(); + p.OffsetX(data, w / 2); // top-right point + maskValueTopRight = p.Pixel(); + p = rowStart; + p.OffsetY(data, h / 2); + p.OffsetX(data, w / 4); // bottom-left point + maskValueBottomLeft = p.Pixel(); + p.OffsetX(data, w / 2); // bottom-right point + maskValueBottomRight = p.Pixel(); + } + REQUIRE(bmpMask.GetDepth() == 1); + CHECK(maskValueTopLeft == true); + CHECK(maskValueTopRight == true); + CHECK(maskValueBottomLeft == false); + CHECK(maskValueBottomRight == false); +#endif // __WXMSW__ + wxBitmap subBmpMask = subBmp.GetMask()->GetBitmap(); // Check sub bitmap mask attributes REQUIRE(subBmpMask.GetWidth() == subBmp.GetWidth()); @@ -758,6 +839,7 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona REQUIRE_FALSE(subBmpMask.GetMask()); // Check sub bitmap mask pixels { + REQUIRE(subBmpMask.GetDepth() == 1); wxNativePixelData data(subBmpMask); REQUIRE(data); wxNativePixelData::Iterator p(data); @@ -774,6 +856,30 @@ TEST_CASE("BitmapTestCase::SubBitmapNonAlphaWithMask", "[bitmap][subbitmap][nona p.OffsetX(data, w2 / 2); // bottom-right point ASSERT_EQUAL_COLOUR_RGB(p, maskClrBottomRight); } + REQUIRE(subBmpMask.GetDepth() == 1); + + // wxMonoPixelData only exists in wxMSW +#if defined(__WXMSW__) + { + REQUIRE(subBmpMask.GetDepth() == 1); + wxMonoPixelData data(subBmpMask); + REQUIRE(data); + wxMonoPixelData::Iterator p(data); + p.OffsetY(data, h2 / 4); + wxMonoPixelData::Iterator rowStart = p; + p.OffsetX(data, w2 / 4); // top-left point + CHECK(p.Pixel() == maskValueTopLeft); + p.OffsetX(data, w2 / 2); // top-right point + CHECK(p.Pixel() == maskValueTopRight); + p = rowStart; + p.OffsetY(data, h2 / 2); + p.OffsetX(data, w2 / 4); // bottom-left point + CHECK(p.Pixel() == maskValueBottomLeft); + p.OffsetX(data, w2 / 2); // bottom-right point + CHECK(p.Pixel() == maskValueBottomRight); + } + REQUIRE(subBmpMask.GetDepth() == 1); +#endif // __WXMSW__ } TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][withmask]") @@ -870,6 +976,7 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][ wxColour maskClrBottomRight; // Fetch sample original mask pixels { + REQUIRE(bmpMask.GetDepth() == 1); wxNativePixelData data(bmpMask); REQUIRE(data); wxNativePixelData::Iterator p(data); @@ -886,11 +993,44 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][ p.OffsetX(data, w / 2); // bottom-right point maskClrBottomRight = wxColour(p.Red(), p.Green(), p.Blue()); } + REQUIRE(bmpMask.GetDepth() == 1); CHECK(maskClrTopLeft == *wxWHITE); CHECK(maskClrTopRight == *wxWHITE); CHECK(maskClrBottomLeft == *wxBLACK); CHECK(maskClrBottomRight == *wxBLACK); + // wxMonoPixelData only exists in wxMSW +#if defined(__WXMSW__) + bool maskValueTopLeft; + bool maskValueTopRight; + bool maskValueBottomLeft; + bool maskValueBottomRight; + // Fetch sample original mask pixels + { + REQUIRE(bmpMask.GetDepth() == 1); + wxMonoPixelData data(bmpMask); + REQUIRE(data); + wxMonoPixelData::Iterator p(data); + p.OffsetY(data, h / 4); + wxMonoPixelData::Iterator rowStart = p; + p.OffsetX(data, w / 4); // top-left point + maskValueTopLeft = p.Pixel(); + p.OffsetX(data, w / 2); // top-right point + maskValueTopRight = p.Pixel(); + p = rowStart; + p.OffsetY(data, h / 2); + p.OffsetX(data, w / 4); // bottom-left point + maskValueBottomLeft = p.Pixel(); + p.OffsetX(data, w / 2); // bottom-right point + maskValueBottomRight = p.Pixel(); + } + REQUIRE(bmpMask.GetDepth() == 1); + CHECK(maskValueTopLeft == true); + CHECK(maskValueTopRight == true); + CHECK(maskValueBottomLeft == false); + CHECK(maskValueBottomRight == false); +#endif // __WXMSW__ + wxBitmap subBmpMask = subBmp.GetMask()->GetBitmap(); // Check sub bitmap mask attributes REQUIRE(subBmpMask.GetWidth() == subBmp.GetWidth()); @@ -902,6 +1042,7 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][ REQUIRE_FALSE(subBmpMask.GetMask()); // Check sub bitmap mask pixels { + REQUIRE(subBmpMask.GetDepth() == 1); wxNativePixelData data(subBmpMask); REQUIRE(data); wxNativePixelData::Iterator p(data); @@ -918,6 +1059,30 @@ TEST_CASE("BitmapTestCase::SubBitmapAlphaWithMask", "[bitmap][subbitmap][alpha][ p.OffsetX(data, w2 / 2); // bottom-right point ASSERT_EQUAL_RGB(p, maskClrBottomRight.Red(), maskClrBottomRight.Green(), maskClrBottomRight.Blue()); } + REQUIRE(subBmpMask.GetDepth() == 1); + + // wxMonoPixelData only exists in wxMSW +#if defined(__WXMSW__) + { + REQUIRE(subBmpMask.GetDepth() == 1); + wxMonoPixelData data(subBmpMask); + REQUIRE(data); + wxMonoPixelData::Iterator p(data); + p.OffsetY(data, h2 / 4); + wxMonoPixelData::Iterator rowStart = p; + p.OffsetX(data, w2 / 4); // top-left point + CHECK(p.Pixel() == maskValueTopLeft); + p.OffsetX(data, w2 / 2); // top-right point + CHECK(p.Pixel() == maskValueTopRight); + p = rowStart; + p.OffsetY(data, h2 / 2); + p.OffsetX(data, w2 / 4); // bottom-left point + CHECK(p.Pixel() == maskValueBottomLeft); + p.OffsetX(data, w2 / 2); // bottom-right point + CHECK(p.Pixel() == maskValueBottomRight); + } + REQUIRE(subBmpMask.GetDepth() == 1); +#endif // __WXMSW__ } namespace Catch