Containers::Array<unsigned char> TgaImageConverter::doExportToData(const ImageReference2D& image) const { #ifndef MAGNUM_TARGET_GLES if(image.format() != ColorFormat::BGR && image.format() != ColorFormat::BGRA && image.format() != ColorFormat::Red) #else if(image.format() != ColorFormat::RGB && image.format() != ColorFormat::RGBA && image.format() != ColorFormat::Red) #endif { Error() << "Trade::TgaImageConverter::convertToData(): unsupported image format" << image.format(); return nullptr; } if(image.type() != ColorType::UnsignedByte) { Error() << "Trade::TgaImageConverter::convertToData(): unsupported image type" << image.type(); return nullptr; } /* Initialize data buffer */ const auto pixelSize = UnsignedByte(image.pixelSize()); auto data = Containers::Array<unsigned char>::zeroInitialized(sizeof(TgaHeader) + pixelSize*image.size().product()); /* Fill header */ auto header = reinterpret_cast<TgaHeader*>(data.begin()); header->imageType = image.format() == ColorFormat::Red ? 3 : 2; header->bpp = pixelSize*8; header->width = UnsignedShort(Utility::Endianness::littleEndian(image.size().x())); header->height = UnsignedShort(Utility::Endianness::littleEndian(image.size().y())); /* Fill data */ std::copy(image.data(), image.data()+pixelSize*image.size().product(), data.begin()+sizeof(TgaHeader)); #ifdef MAGNUM_TARGET_GLES if(image.format() == ColorFormat::RGB) { auto pixels = reinterpret_cast<Math::Vector3<UnsignedByte>*>(data.begin()+sizeof(TgaHeader)); std::transform(pixels, pixels + image.size().product(), pixels, [](Math::Vector3<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r'>(pixel); }); } else if(image.format() == ColorFormat::RGBA) { auto pixels = reinterpret_cast<Math::Vector4<UnsignedByte>*>(data.begin()+sizeof(TgaHeader)); std::transform(pixels, pixels + image.size().product(), pixels, [](Math::Vector4<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r', 'a'>(pixel); }); } #endif return std::move(data); }
Containers::Array<char> TgaImageConverter::doExportToData(const ImageView2D& image) { #ifndef MAGNUM_TARGET_GLES if(image.storage().swapBytes()) { Error() << "Trade::TgaImageConverter::exportToData(): pixel byte swap is not supported"; return nullptr; } #endif if(image.format() != PixelFormat::RGB && image.format() != PixelFormat::RGBA #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) && image.format() != PixelFormat::Red #endif #ifdef MAGNUM_TARGET_GLES2 && image.format() != PixelFormat::Luminance #endif ) { Error() << "Trade::TgaImageConverter::exportToData(): unsupported color format" << image.format(); return nullptr; } if(image.type() != PixelType::UnsignedByte) { Error() << "Trade::TgaImageConverter::exportToData(): unsupported color type" << image.type(); return nullptr; } /* Initialize data buffer */ const auto pixelSize = UnsignedByte(image.pixelSize()); Containers::Array<char> data{Containers::ValueInit, sizeof(TgaHeader) + pixelSize*image.size().product()}; /* Fill header */ auto header = reinterpret_cast<TgaHeader*>(data.begin()); switch(image.format()) { case PixelFormat::RGB: case PixelFormat::RGBA: header->imageType = 2; break; #if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2)) case PixelFormat::Red: #endif #ifdef MAGNUM_TARGET_GLES2 case PixelFormat::Luminance: #endif header->imageType = 3; break; default: CORRADE_ASSERT_UNREACHABLE(); } header->bpp = pixelSize*8; header->width = UnsignedShort(Utility::Endianness::littleEndian(image.size().x())); header->height = UnsignedShort(Utility::Endianness::littleEndian(image.size().y())); /* Image data pointer including skip */ const char* imageData = image.data() + std::get<0>(image.dataProperties()); /* Fill data or copy them row by row if we need to drop the padding */ const std::size_t rowSize = image.size().x()*pixelSize; const std::size_t rowStride = std::get<1>(image.dataProperties()).x(); if(rowStride != rowSize) { for(std::int_fast32_t y = 0; y != image.size().y(); ++y) std::copy_n(imageData + y*rowStride, rowSize, data.begin() + sizeof(TgaHeader) + y*rowSize); } else std::copy_n(imageData, pixelSize*image.size().product(), data.begin() + sizeof(TgaHeader)); if(image.format() == PixelFormat::RGB) { auto pixels = reinterpret_cast<Math::Vector3<UnsignedByte>*>(data.begin()+sizeof(TgaHeader)); std::transform(pixels, pixels + image.size().product(), pixels, [](Math::Vector3<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r'>(pixel); }); } else if(image.format() == PixelFormat::RGBA) { auto pixels = reinterpret_cast<Math::Vector4<UnsignedByte>*>(data.begin()+sizeof(TgaHeader)); std::transform(pixels, pixels + image.size().product(), pixels, [](Math::Vector4<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r', 'a'>(pixel); }); } return data; }
std::tuple<std::vector<Vertex>, Range2D> renderVerticesInternal(AbstractFont& font, const GlyphCache& cache, const Float size, const std::string& text, const Alignment alignment) { /* Output data, reserve memory as when the text would be ASCII-only. In reality the actual vertex count will be smaller, but allocating more at once is better than reallocating many times later. */ std::vector<Vertex> vertices; vertices.reserve(text.size()*4); /* Total rendered bounds, intial line position, line increment, last+1 vertex on previous line */ Range2D rectangle; Vector2 linePosition; const Vector2 lineAdvance = Vector2::yAxis(font.lineHeight()*size/font.size()); std::size_t lastLineLastVertex = 0; /* Temp buffer so we don't allocate for each new line */ /** * @todo C++1z: use std::string_view to avoid the one allocation and all * the copying altogether */ std::string line; line.reserve(text.size()); /* Render each line separately and align it horizontally */ std::size_t pos, prevPos = 0; do { /* Empty line, nothing to do (the rest is done below in while expression) */ if((pos = text.find('\n', prevPos)) == prevPos) continue; /* Copy the line into the temp buffer */ line.assign(text, prevPos, pos-prevPos); /* Layout the line */ const auto layouter = font.layout(cache, size, line); const UnsignedInt vertexCount = layouter->glyphCount()*4; /* Verify that we don't reallocate anything. The only problem might arise when the layouter decides to compose one character from more than one glyph (i.e. accents). Will remove the assert when this issue arises. */ CORRADE_INTERNAL_ASSERT(vertices.size()+vertexCount <= vertices.capacity()); /* Bounds of rendered line */ Range2D lineRectangle; /* Render all glyphs */ Vector2 cursorPosition(linePosition); for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { Range2D quadPosition, textureCoordinates; std::tie(quadPosition, textureCoordinates) = layouter->renderGlyph(i, cursorPosition, lineRectangle); /* 0---2 | | | | | | 1---3 */ vertices.insert(vertices.end(), { {quadPosition.topLeft(), textureCoordinates.topLeft()}, {quadPosition.bottomLeft(), textureCoordinates.bottomLeft()}, {quadPosition.topRight(), textureCoordinates.topRight()}, {quadPosition.bottomRight(), textureCoordinates.bottomRight()} }); } /** @todo What about top-down text? */ /* Horizontally align the rendered line */ Float alignmentOffsetX = 0.0f; if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentCenter) alignmentOffsetX = -lineRectangle.centerX(); else if((UnsignedByte(alignment) & Implementation::AlignmentHorizontal) == Implementation::AlignmentRight) alignmentOffsetX = -lineRectangle.right(); /* Integer alignment */ if(UnsignedByte(alignment) & Implementation::AlignmentIntegral) alignmentOffsetX = Math::round(alignmentOffsetX); /* Align positions and bounds on current line */ lineRectangle = lineRectangle.translated(Vector2::xAxis(alignmentOffsetX)); for(auto it = vertices.begin()+lastLineLastVertex; it != vertices.end(); ++it) it->position.x() += alignmentOffsetX; /* Add final line bounds to total bounds, similarly to AbstractFont::renderGlyph() */ if(!rectangle.size().isZero()) { rectangle.bottomLeft() = Math::min(rectangle.bottomLeft(), lineRectangle.bottomLeft()); rectangle.topRight() = Math::max(rectangle.topRight(), lineRectangle.topRight()); } else rectangle = lineRectangle; /* Move to next line */ } while(prevPos = pos+1, linePosition -= lineAdvance, lastLineLastVertex = vertices.size(), pos != std::string::npos); /* Vertically align the rendered text */ Float alignmentOffsetY = 0.0f; if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentMiddle) alignmentOffsetY = -rectangle.centerY(); else if((UnsignedByte(alignment) & Implementation::AlignmentVertical) == Implementation::AlignmentTop) alignmentOffsetY = -rectangle.top(); /* Integer alignment */ if(UnsignedByte(alignment) & Implementation::AlignmentIntegral) alignmentOffsetY = Math::round(alignmentOffsetY); /* Align positions and bounds */ rectangle = rectangle.translated(Vector2::yAxis(alignmentOffsetY)); for(auto& v: vertices) v.position.y() += alignmentOffsetY; return std::make_tuple(std::move(vertices), rectangle); }