michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TextRenderer.h" michael@0: #include "FontData.h" michael@0: #include "png.h" michael@0: #include "mozilla/Base64.h" michael@0: #include "mozilla/layers/Compositor.h" michael@0: #include "mozilla/layers/TextureHost.h" michael@0: #include "mozilla/layers/Effects.h" michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: using namespace gfx; michael@0: using namespace std; michael@0: michael@0: const Float sBackgroundOpacity = 0.6f; michael@0: const SurfaceFormat sTextureFormat = SurfaceFormat::B8G8R8A8; michael@0: michael@0: static void PNGAPI info_callback(png_structp png_ptr, png_infop info_ptr) michael@0: { michael@0: png_read_update_info(png_ptr, info_ptr); michael@0: } michael@0: michael@0: static void PNGAPI row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) michael@0: { michael@0: MOZ_ASSERT(sTextureFormat == SurfaceFormat::B8G8R8A8); michael@0: michael@0: DataSourceSurface::MappedSurface map = static_cast(png_get_progressive_ptr(png_ptr))->GetSurfaceMap(); michael@0: michael@0: uint32_t* dst = (uint32_t*)(map.mData + map.mStride * row_num); michael@0: michael@0: for (uint32_t x = 0; x < sTextureWidth; x++) { michael@0: // We blend to a transparent white background, this will make text readable michael@0: // even if it's on a dark background. Without hurting our ability to michael@0: // interact with the content behind the text. michael@0: Float alphaValue = Float(0xFF - new_row[x]) / 255.0f; michael@0: Float baseValue = sBackgroundOpacity * (1.0f - alphaValue); michael@0: Color pixelColor(baseValue, baseValue, baseValue, baseValue + alphaValue); michael@0: dst[x] = pixelColor.ToABGR(); michael@0: } michael@0: } michael@0: michael@0: TextRenderer::~TextRenderer() michael@0: { michael@0: if (mGlyphBitmaps) { michael@0: mGlyphBitmaps->Unmap(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: TextRenderer::RenderText(const string& aText, const IntPoint& aOrigin, michael@0: const Matrix4x4& aTransform, uint32_t aTextSize, michael@0: uint32_t aTargetPixelWidth) michael@0: { michael@0: EnsureInitialized(); michael@0: michael@0: // For now we only have a bitmap font with a 16px cell size, so we just michael@0: // scale it up if the user wants larger text. michael@0: Float scaleFactor = Float(aTextSize) / Float(sCellHeight); michael@0: michael@0: aTargetPixelWidth /= scaleFactor; michael@0: michael@0: uint32_t numLines = 1; michael@0: uint32_t maxWidth = 0; michael@0: uint32_t lineWidth = 0; michael@0: // Calculate the size of the surface needed to draw all the glyphs. michael@0: for (uint32_t i = 0; i < aText.length(); i++) { michael@0: // Insert a line break if we go past the TargetPixelWidth. michael@0: // XXX - this has the downside of overrunning the intended width, causing michael@0: // things at the edge of a window to be cut off. michael@0: if (aText[i] == '\n' || (aText[i] == ' ' && lineWidth > aTargetPixelWidth)) { michael@0: numLines++; michael@0: lineWidth = 0; michael@0: continue; michael@0: } michael@0: michael@0: lineWidth += sGlyphWidths[uint32_t(aText[i])]; michael@0: maxWidth = std::max(lineWidth, maxWidth); michael@0: } michael@0: michael@0: // Create a surface to draw our glyphs to. michael@0: RefPtr textSurf = michael@0: Factory::CreateDataSourceSurface(IntSize(maxWidth, numLines * sCellHeight), sTextureFormat); michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: textSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map); michael@0: michael@0: // Initialize the surface to transparent white. michael@0: memset(map.mData, uint8_t(sBackgroundOpacity * 255.0f), michael@0: numLines * sCellHeight * map.mStride); michael@0: michael@0: uint32_t currentXPos = 0; michael@0: uint32_t currentYPos = 0; michael@0: michael@0: // Copy our glyphs onto the surface. michael@0: for (uint32_t i = 0; i < aText.length(); i++) { michael@0: if (aText[i] == '\n' || (aText[i] == ' ' && currentXPos > aTargetPixelWidth)) { michael@0: currentYPos += sCellHeight; michael@0: currentXPos = 0; michael@0: continue; michael@0: } michael@0: michael@0: uint32_t glyphXOffset = aText[i] % (sTextureWidth / sCellWidth) * sCellWidth * BytesPerPixel(sTextureFormat); michael@0: uint32_t truncatedLine = aText[i] / (sTextureWidth / sCellWidth); michael@0: uint32_t glyphYOffset = truncatedLine * sCellHeight * mMap.mStride; michael@0: michael@0: for (int y = 0; y < 16; y++) { michael@0: memcpy(map.mData + (y + currentYPos) * map.mStride + currentXPos * BytesPerPixel(sTextureFormat), michael@0: mMap.mData + glyphYOffset + y * mMap.mStride + glyphXOffset, michael@0: sGlyphWidths[uint32_t(aText[i])] * BytesPerPixel(sTextureFormat)); michael@0: } michael@0: michael@0: currentXPos += sGlyphWidths[uint32_t(aText[i])]; michael@0: } michael@0: michael@0: textSurf->Unmap(); michael@0: michael@0: RefPtr src = mCompositor->CreateDataTextureSource(); michael@0: michael@0: if (!src->Update(textSurf)) { michael@0: // Upload failed. michael@0: return; michael@0: } michael@0: michael@0: RefPtr effect = new EffectRGB(src, true, Filter::LINEAR); michael@0: EffectChain chain; michael@0: chain.mPrimaryEffect = effect; michael@0: michael@0: Matrix4x4 transform = aTransform; michael@0: transform.Scale(scaleFactor, scaleFactor, 1.0f); michael@0: mCompositor->DrawQuad(Rect(aOrigin.x, aOrigin.y, maxWidth, numLines * 16), michael@0: Rect(-10000, -10000, 20000, 20000), chain, 1.0f, transform); michael@0: } michael@0: michael@0: void michael@0: TextRenderer::EnsureInitialized() michael@0: { michael@0: if (mGlyphBitmaps) { michael@0: return; michael@0: } michael@0: michael@0: mGlyphBitmaps = Factory::CreateDataSourceSurface(IntSize(sTextureWidth, sTextureHeight), sTextureFormat); michael@0: michael@0: mGlyphBitmaps->Map(DataSourceSurface::MapType::READ_WRITE, &mMap); michael@0: michael@0: png_structp png_ptr = NULL; michael@0: png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); michael@0: michael@0: png_set_progressive_read_fn(png_ptr, this, info_callback, row_callback, nullptr); michael@0: png_infop info_ptr = NULL; michael@0: info_ptr = png_create_info_struct(png_ptr); michael@0: michael@0: png_process_data(png_ptr, info_ptr, (uint8_t*)sFontPNG, sizeof(sFontPNG)); michael@0: michael@0: png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); michael@0: } michael@0: michael@0: } michael@0: }