Skip to content

Commit

Permalink
Rendering for larger than 100pt emoji on macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
weisJ committed Sep 9, 2024
1 parent c1d29e6 commit ddae75d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,20 @@

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.logging.Logger;

import org.jetbrains.annotations.NotNull;

import com.github.weisj.jsvg.attributes.font.SVGFont;
import com.github.weisj.jsvg.renderer.Graphics2DOutput;
import com.github.weisj.jsvg.renderer.Output;
import com.github.weisj.jsvg.util.ImageUtil;
import com.github.weisj.jsvg.util.SystemUtil;

public class AbstractGlyphRun<T extends Shape> {
private static final Logger LOGGER = Logger.getLogger(AbstractGlyphRun.class.getName());
private final @NotNull T shape;
private final @NotNull List<@NotNull PaintableEmoji> emojis;

Expand All @@ -53,12 +62,37 @@ public static class PaintableEmoji {
this.transform = transform;
}

public @NotNull EmojiGlyph glyph() {
return glyph;
}
public void render(@NotNull Output output, @NotNull SVGFont font) {
output.applyTransform(transform);
int fontSize = font.size();

// The JDK text pipeline on macOS can only render Emoji up to 100pt.
// In this case we have to paint to a buffer which is then scaled up.
int maxFontSize = SystemUtil.isMacOS ? 100 : Integer.MAX_VALUE;

if (output.transform().getScaleY() * fontSize > maxFontSize) {
// Guess where the baseline ought to be for descenders to be fully visible. Very hackish.
float baselinePosition = 0.9f;
if (glyph.largeBitmap == null) {
// Size of bitmap is guessed, as there is now reliable way to get the size of the glyph.
BufferedImage bitmap = ImageUtil.createCompatibleTransparentImage(maxFontSize, maxFontSize);
Graphics g = bitmap.getGraphics();
g.setFont(g.getFont().deriveFont((float) maxFontSize));

public @NotNull AffineTransform transform() {
return transform;
g.drawString(glyph.codepoint(), 0, (int) (baselinePosition * maxFontSize));
g.dispose();
glyph.largeBitmap = bitmap;
}
output.scale((double) fontSize / maxFontSize, (double) fontSize / maxFontSize);
output.translate(0, -(int) (baselinePosition * maxFontSize));
output.drawImage(glyph.largeBitmap);
} else {
if (output instanceof Graphics2DOutput) {
Graphics2D g = ((Graphics2DOutput) output).graphics();
g.setFont(g.getFont().deriveFont((float) fontSize));
g.drawString(glyph.codepoint(), 0, 0);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@
package com.github.weisj.jsvg.nodes.text;

import java.awt.*;
import java.awt.image.BufferedImage;

import org.jetbrains.annotations.NotNull;

public class EmojiGlyph extends Glyph {
private final @NotNull String codepoint;
BufferedImage largeBitmap;

public EmojiGlyph(@NotNull String codepoint, float advance) {
super(new Rectangle(), advance, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,11 @@ static void renderGlyphRun(@NotNull Output output, @NotNull PaintOrder paintOrde
null);

// Experimental Emoji rendering
if (output instanceof Graphics2DOutput) {
Graphics2DOutput g2dOutput = (Graphics2DOutput) output;
Graphics2D g = (Graphics2D) g2dOutput.graphics().create();
AffineTransform transform = g.getTransform();

g.setFont(g.getFont().deriveFont((float) context.font().size()));
for (AbstractGlyphRun.PaintableEmoji emoji : glyphRun.emojis()) {
g.setTransform(transform);
g.transform(emoji.transform());
g.drawString(emoji.glyph().codepoint(), 0, 0);
}

g.dispose();
SVGFont font = context.font();
Output.SafeState safeState = output.safeState();
for (AbstractGlyphRun.PaintableEmoji emoji : glyphRun.emojis()) {
emoji.render(output, font);
safeState.restore();
}

// Invalidate the glyphRun. Avoids holding onto the RenderContext, which may reference a JComponent.
Expand Down
30 changes: 30 additions & 0 deletions jsvg/src/main/java/com/github/weisj/jsvg/util/SystemUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* MIT License
*
* Copyright (c) 2024 Jannis Weis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
package com.github.weisj.jsvg.util;

public final class SystemUtil {

private SystemUtil() {}

public static final String OS_NAME = System.getProperty("os.name").toLowerCase();
public static final boolean isMacOS = OS_NAME.contains("mac");
}

0 comments on commit ddae75d

Please sign in to comment.