HRESULT SaveBitmap2(bitmap_argb32 &bitmap, const WCHAR *filename) { HRESULT result; // Convert the bitmap into a form we understand and save it std::unique_ptr<UINT32> pBitmap(new UINT32[bitmap.width() * bitmap.height()]); for (int y = 0; y < bitmap.height(); y++) { UINT32* pRow = pBitmap.get() + (y * bitmap.width()); for (int x = 0; x < bitmap.width(); x++) { UINT32 pixel = bitmap.pix32(y, x); pRow[x] = (pixel == 0xFFFFFFFF) ? rgb_t(0xFF, 0x00, 0x00, 0x00) : rgb_t(0xFF, 0xFF, 0xFF, 0xFF); } } ComPtr<IWICImagingFactory> wicFactory; HR_RETHR(CoCreateInstance( CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), (void**)&wicFactory)); // Save bitmap ComPtr<IWICBitmap> bmp2 = nullptr; wicFactory->CreateBitmapFromMemory( bitmap.width(), bitmap.height(), GUID_WICPixelFormat32bppRGBA, bitmap.width() * sizeof(UINT32), bitmap.width() * bitmap.height() * sizeof(UINT32), (BYTE*)pBitmap.get(), &bmp2); SaveBitmap(bmp2.Get(), GUID_WICPixelFormat32bppRGBA, filename); return S_OK; }
void ui_menu::render_triangle(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param) { int halfwidth = dest.width() / 2; int height = dest.height(); int x, y; // start with all-transparent dest.fill(rgb_t(0x00,0x00,0x00,0x00)); // render from the tip to the bottom for (y = 0; y < height; y++) { int linewidth = (y * (halfwidth - 1) + (height / 2)) * 255 * 2 / height; UINT32 *target = &dest.pix32(y, halfwidth); // don't antialias if height < 12 if (dest.height() < 12) { int pixels = (linewidth + 254) / 255; if (pixels % 2 == 0) pixels++; linewidth = pixels * 255; } // loop while we still have data to generate for (x = 0; linewidth > 0; x++) { int dalpha; // first column we only consume one pixel if (x == 0) { dalpha = MIN(0xff, linewidth); target[x] = rgb_t(dalpha,0xff,0xff,0xff); } // remaining columns consume two pixels, one on each side else { dalpha = MIN(0x1fe, linewidth); target[x] = target[-x] = rgb_t(dalpha/2,0xff,0xff,0xff); } // account for the weight we consumed */ linewidth -= dalpha; } } }
virtual bool get_bitmap(unicode_char chnum, bitmap_argb32 &bitmap, std::int32_t &width, std::int32_t &xoffs, std::int32_t &yoffs) override { const int MEM_ALIGN_CONST = 31; const int BITMAP_PAD = 50; HRESULT result; UINT cbData; BYTE* pixels = nullptr; ComPtr<ID2D1BitmapRenderTarget> target; ComPtr<ID2D1SolidColorBrush> pWhiteBrush; ComPtr<IWICBitmap> wicBitmap; ComPtr<IWICBitmapLock> lock; ComPtr<IDWriteFontFace> face; HR_RET0(m_font->CreateFontFace(face.GetAddressOf())); // get the GDI metrics DWRITE_FONT_METRICS gdi_metrics; HR_RET0(face->GetGdiCompatibleMetrics( m_fontEmHeightInDips, 1.0f, nullptr, &gdi_metrics)); FontDimensionFactory fdf(gdi_metrics.designUnitsPerEm, m_fontEmHeightInDips); UINT32 tempChar = chnum; UINT16 glyphIndex; HR_RET0(face->GetGlyphIndicesW(&tempChar, 1, &glyphIndex)); // get the width of this character DWRITE_GLYPH_METRICS glyph_metrics = { 0 }; HR_RET0(face->GetGdiCompatibleGlyphMetrics( m_fontEmHeightInDips, 1.0f, nullptr, FALSE, &glyphIndex, 1, &glyph_metrics)); // The height is the ascent added to the descent // By definition, the Em is equal to Cell Height minus Internal Leading (topSide bearing). //auto cellheight = fdf.FromDesignUnit(gdi_metrics.ascent + gdi_metrics.descent + gdi_metrics.); auto ascent = fdf.FromDesignUnit(gdi_metrics.ascent); auto descent = fdf.FromDesignUnit(gdi_metrics.descent); auto charHeight = ascent + descent; auto abc = fdf.CreateAbcWidths( glyph_metrics.advanceWidth, glyph_metrics.leftSideBearing, glyph_metrics.rightSideBearing); width = abc.abcA().Dips() + abc.abcB().Dips() + abc.abcC().Dips(); // determine desired bitmap size int bmwidth = (BITMAP_PAD + abc.abcA().Dips() + abc.abcB().Dips() + abc.abcC().Dips() + BITMAP_PAD + MEM_ALIGN_CONST) & ~MEM_ALIGN_CONST; int bmheight = BITMAP_PAD + charHeight.Dips() + BITMAP_PAD; // GUID_WICPixelFormat8bppAlpha is 8 bits per pixel const REFWICPixelFormatGUID source_bitmap_wic_format = GUID_WICPixelFormat8bppAlpha; const DXGI_FORMAT source_bitmap_dxgi_format = DXGI_FORMAT_A8_UNORM; const D2D1_ALPHA_MODE source_bitmap_d2d_alpha_mode = D2D1_ALPHA_MODE_STRAIGHT; // describe the bitmap we want HR_RET0(m_wicFactory->CreateBitmap( bmwidth, bmheight, source_bitmap_wic_format, WICBitmapCacheOnLoad, wicBitmap.GetAddressOf())); D2D1_RENDER_TARGET_PROPERTIES targetProps; targetProps = D2D1::RenderTargetProperties(); targetProps.pixelFormat = D2D1::PixelFormat(source_bitmap_dxgi_format, source_bitmap_d2d_alpha_mode); // create a DIB to render to HR_RET0(this->m_d2dfactory->CreateWicBitmapRenderTarget( wicBitmap.Get(), &targetProps, reinterpret_cast<ID2D1RenderTarget**>(target.GetAddressOf()))); target->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); // Create our brush HR_RET0(target->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), pWhiteBrush.GetAddressOf())); // Signal the start of the frame target->BeginDraw(); // clear the bitmap // In the alpha mask, it will look like 0x00 per pixel target->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f)); // now draw the character DWRITE_GLYPH_RUN run = { nullptr }; DWRITE_GLYPH_OFFSET offsets; offsets.advanceOffset = 0; offsets.ascenderOffset = 0; float advanceWidth = abc.advanceWidth().Dips(); run.fontEmSize = m_fontEmHeightInDips; run.fontFace = face.Get(); run.glyphCount = 1; run.glyphIndices = &glyphIndex; run.glyphAdvances = &advanceWidth; run.glyphOffsets = &offsets; auto baseline_origin = D2D1::Point2F(BITMAP_PAD + abc.abcA().Dips() + 1, BITMAP_PAD + ascent.Dips()); target->DrawGlyphRun( baseline_origin, &run, pWhiteBrush.Get(), DWRITE_MEASURING_MODE_GDI_CLASSIC); HR_RET0(target->EndDraw()); #ifdef DWRITE_DEBUGGING // Save to file for debugging SaveBitmap(wicBitmap.Get(), GUID_WICPixelFormatBlackWhite, L"C:\\temp\\ddraw_step1.bmp"); #endif // characters are expected to be full-height rectangle actbounds; actbounds.min_y = BITMAP_PAD; actbounds.max_y = BITMAP_PAD + charHeight.Dips() - 1; // Lock the bitmap and get the data pointer WICRect rect = { 0, 0, bmwidth, bmheight }; HR_RET0(wicBitmap->Lock(&rect, WICBitmapLockRead, lock.GetAddressOf())); HR_RET0(lock->GetDataPointer(&cbData, static_cast<BYTE**>(&pixels))); // determine the actual left of the character for (actbounds.min_x = 0; actbounds.min_x < bmwidth; actbounds.min_x++) { BYTE *offs = pixels + actbounds.min_x; UINT8 summary = 0; for (int y = 0; y < bmheight; y++) summary |= offs[y * bmwidth]; if (summary != 0) { break; } } // determine the actual right of the character // Start from the right edge, and move in until we find a pixel for (actbounds.max_x = bmwidth - 1; actbounds.max_x >= 0; actbounds.max_x--) { BYTE *offs = pixels + actbounds.max_x; UINT8 summary = 0; // Go through the entire column and build a summary for (int y = 0; y < bmheight; y++) summary |= offs[y * bmwidth]; if (summary != 0) { break; } } // allocate a new bitmap if (actbounds.max_x >= actbounds.min_x && actbounds.max_y >= actbounds.min_y) { bitmap.allocate(actbounds.max_x + 1 - actbounds.min_x, actbounds.max_y + 1 - actbounds.min_y); // copy the bits into it for (int y = 0; y < bitmap.height(); y++) { UINT32 *dstrow = &bitmap.pix32(y); UINT8 *srcrow = &pixels[(y + actbounds.min_y) * bmwidth]; for (int x = 0; x < bitmap.width(); x++) { int effx = x + actbounds.min_x; dstrow[x] = rgb_t(srcrow[effx], 0xff, 0xff, 0xff); } } // set the final offset values xoffs = actbounds.min_x - (BITMAP_PAD + abc.abcA().Dips()); yoffs = actbounds.max_y - (BITMAP_PAD + ascent.Dips()); #ifdef DWRITE_DEBUGGING SaveBitmap2(bitmap, L"C:\\temp\\dwrite_final.bmp"); #endif } BOOL success = bitmap.valid(); #ifdef DWRITE_DEBUGGING osd_printf_debug( "dwr: %s, c'%S' w%i x%i y%i asc%i dsc%i a%ib%ic%i\n", success ? "Success" : "Error", (WCHAR*)&chnum, width, xoffs, yoffs, ascent.Dips(), descent.Dips(), abc.abcA().Dips(), abc.abcB().Dips(), abc.abcC().Dips()); #endif return success; }
static bool bitmap_to_chars(bitmap_argb32 &bitmap, render_font &font) { // loop over rows int rowstart = 0; while (rowstart < bitmap.height()) { // find the top of the row for ( ; rowstart < bitmap.height(); rowstart++) if (pixel_is_set(bitmap, rowstart, 0)) break; if (rowstart >= bitmap.height()) break; // find the bottom of the row int rowend; for (rowend = rowstart + 1; rowend < bitmap.height(); rowend++) if (!pixel_is_set(bitmap, rowend, 0)) { rowend--; break; } // find the baseline int baseline; for (baseline = rowstart; baseline <= rowend; baseline++) if (pixel_is_set(bitmap, baseline, 1)) break; if (baseline > rowend) { fprintf(stderr, "No baseline found between rows %d-%d\n", rowstart, rowend); break; } // set or confirm the height if (font.height == 0) { font.height = rowend - rowstart + 1; font.yoffs = baseline - rowend; } else { if (font.height != rowend - rowstart + 1) { fprintf(stderr, "Inconsistent font height at rows %d-%d\n", rowstart, rowend); break; } if (font.yoffs != baseline - rowend) { fprintf(stderr, "Inconsistent baseline at rows %d-%d\n", rowstart, rowend); break; } } // decode the starting character int chstart = 0; for (int x = 0; x < 4; x++) for (int y = 0; y < 4; y++) chstart = (chstart << 1) | pixel_is_set(bitmap, rowstart + y, 2 + x); // print info // printf("Row %d-%d, baseline %d, character start %X\n", rowstart, rowend, baseline, chstart); // scan the column to find characters int colstart = 0; while (colstart < bitmap.width()) { render_font_char &ch = font.chars[chstart]; // find the start of the character for ( ; colstart < bitmap.width(); colstart++) if (pixel_is_set(bitmap, rowend + 2, colstart)) break; if (colstart >= bitmap.width()) break; // find the end of the character int colend; for (colend = colstart + 1; colend < bitmap.width(); colend++) if (!pixel_is_set(bitmap, rowend + 2, colend)) { colend--; break; } // skip char which code is already registered if (ch.width <= 0) { // print info // printf(" Character %X - width = %d\n", chstart, colend - colstart + 1); // allocate a bitmap ch.bitmap = global_alloc(bitmap_argb32(colend - colstart + 1, font.height)); // plot the character for (int y = rowstart; y <= rowend; y++) for (int x = colstart; x <= colend; x++) ch.bitmap->pix32(y - rowstart, x - colstart) = pixel_is_set(bitmap, y, x) ? 0xffffffff : 0x00000000; // set the character parameters ch.width = colend - colstart + 1; ch.xoffs = 0; ch.yoffs = font.yoffs; ch.bmwidth = ch.bitmap->width(); ch.bmheight = ch.bitmap->height(); } // next character chstart++; colstart = colend + 1; } // next row rowstart = rowend + 1; } // return non-zero (TRUE) if we errored return (rowstart < bitmap.height()); }
bool osd_font_windows::get_bitmap(unicode_char chnum, bitmap_argb32 &bitmap, INT32 &width, INT32 &xoffs, INT32 &yoffs) { // create a dummy DC to work with HDC dummyDC = CreateCompatibleDC(NULL); HGDIOBJ oldfont = SelectObject(dummyDC, m_font); // get the text metrics TEXTMETRIC metrics = { 0 }; GetTextMetrics(dummyDC, &metrics); // get the width of this character ABC abc; if (!GetCharABCWidths(dummyDC, chnum, chnum, &abc)) { abc.abcA = 0; abc.abcC = 0; GetCharWidth32(dummyDC, chnum, chnum, reinterpret_cast<LPINT>(&abc.abcB)); } width = abc.abcA + abc.abcB + abc.abcC; // determine desired bitmap size int bmwidth = (50 + abc.abcA + abc.abcB + abc.abcC + 50 + 31) & ~31; int bmheight = 50 + metrics.tmHeight + 50; // describe the bitmap we want BYTE bitmapinfodata[sizeof(BITMAPINFOHEADER)+2 * sizeof(RGBQUAD)] = { 0 }; BITMAPINFO &info = *reinterpret_cast<BITMAPINFO *>(bitmapinfodata); info.bmiHeader.biSize = sizeof(info.bmiHeader); info.bmiHeader.biWidth = bmwidth; info.bmiHeader.biHeight = -bmheight; info.bmiHeader.biPlanes = 1; info.bmiHeader.biBitCount = 1; info.bmiHeader.biCompression = BI_RGB; info.bmiHeader.biSizeImage = 0; info.bmiHeader.biXPelsPerMeter = GetDeviceCaps(dummyDC, HORZRES) / GetDeviceCaps(dummyDC, HORZSIZE); info.bmiHeader.biYPelsPerMeter = GetDeviceCaps(dummyDC, VERTRES) / GetDeviceCaps(dummyDC, VERTSIZE); info.bmiHeader.biClrUsed = 0; info.bmiHeader.biClrImportant = 0; RGBQUAD col1 = info.bmiColors[0]; RGBQUAD col2 = info.bmiColors[1]; col1.rgbBlue = col1.rgbGreen = col1.rgbRed = 0x00; col2.rgbBlue = col2.rgbGreen = col2.rgbRed = 0xff; // create a DIB to render to BYTE *bits; HBITMAP dib = CreateDIBSection(dummyDC, &info, DIB_RGB_COLORS, reinterpret_cast<VOID **>(&bits), NULL, 0); if (dib) { HGDIOBJ oldbitmap = SelectObject(dummyDC, dib); // clear the bitmap int rowbytes = bmwidth / 8; memset(bits, 0, rowbytes * bmheight); // now draw the character WCHAR tempchar = chnum; SetTextColor(dummyDC, RGB(0xff, 0xff, 0xff)); SetBkColor(dummyDC, RGB(0x00, 0x00, 0x00)); ExtTextOutW(dummyDC, 50 + abc.abcA, 50, ETO_OPAQUE, NULL, &tempchar, 1, NULL); // characters are expected to be full-height rectangle actbounds; actbounds.min_y = 50; actbounds.max_y = 50 + metrics.tmHeight - 1; // determine the actual left of the character for (actbounds.min_x = 0; actbounds.min_x < rowbytes; actbounds.min_x++) { BYTE *offs = bits + actbounds.min_x; UINT8 summary = 0; for (int y = 0; y < bmheight; y++) summary |= offs[y * rowbytes]; if (summary != 0) { actbounds.min_x *= 8; if (!(summary & 0x80)) actbounds.min_x++; if (!(summary & 0xc0)) actbounds.min_x++; if (!(summary & 0xe0)) actbounds.min_x++; if (!(summary & 0xf0)) actbounds.min_x++; if (!(summary & 0xf8)) actbounds.min_x++; if (!(summary & 0xfc)) actbounds.min_x++; if (!(summary & 0xfe)) actbounds.min_x++; break; } } // determine the actual right of the character for (actbounds.max_x = rowbytes - 1; actbounds.max_x >= 0; actbounds.max_x--) { BYTE *offs = bits + actbounds.max_x; UINT8 summary = 0; for (int y = 0; y < bmheight; y++) summary |= offs[y * rowbytes]; if (summary != 0) { actbounds.max_x *= 8; if (summary & 0x7f) actbounds.max_x++; if (summary & 0x3f) actbounds.max_x++; if (summary & 0x1f) actbounds.max_x++; if (summary & 0x0f) actbounds.max_x++; if (summary & 0x07) actbounds.max_x++; if (summary & 0x03) actbounds.max_x++; if (summary & 0x01) actbounds.max_x++; break; } } // allocate a new bitmap if (actbounds.max_x >= actbounds.min_x && actbounds.max_y >= actbounds.min_y) { bitmap.allocate(actbounds.max_x + 1 - actbounds.min_x, actbounds.max_y + 1 - actbounds.min_y); // copy the bits into it for (int y = 0; y < bitmap.height(); y++) { UINT32 *dstrow = &bitmap.pix32(y); UINT8 *srcrow = &bits[(y + actbounds.min_y) * rowbytes]; for (int x = 0; x < bitmap.width(); x++) { int effx = x + actbounds.min_x; dstrow[x] = ((srcrow[effx / 8] << (effx % 8)) & 0x80) ? rgb_t(0xff, 0xff, 0xff, 0xff) : rgb_t(0x00, 0xff, 0xff, 0xff); } } // set the final offset values xoffs = actbounds.min_x - (50 + abc.abcA); yoffs = actbounds.max_y - (50 + metrics.tmAscent); } // de-select the font and release the DC SelectObject(dummyDC, oldbitmap); DeleteObject(dib); } SelectObject(dummyDC, oldfont); DeleteDC(dummyDC); return bitmap.valid(); }