Package org.fonteditor.hinter

Source Code of org.fonteditor.hinter.SectionRescaler

package org.fonteditor.hinter;

import org.fonteditor.elements.paths.FEPath;
import org.fonteditor.elements.points.FEPoint;
import org.fonteditor.elements.points.FEPointList;
import org.fonteditor.font.FEFont;
import org.fonteditor.font.FEGlyph;
import org.fonteditor.options.display.DisplayOptions;
import org.fonteditor.sliders.Slider;
import org.fonteditor.sliders.SliderManager;
import org.fonteditor.sliders.Sliders;

/**
* Main hinting class.
* Manages the alignment of vertical and horizontal Glyph path sections.
* It's job is to make sure they fall on pixel boundaries.
*/

public class Hinter {
  private static boolean hint_x = true;
  private static boolean hint_y = true;

  public static void hint(FEGlyph glyph, DisplayOptions gdo) {
    if (gdo.isHint()) {
      final Sliders s = glyph.getSliders();

      // Prevent any shifting off the top or LHS caused by the width...

      final int v_offset = (gdo.getPen().getBottom() > 0) ? -gdo.getLineWidthOffsetNorth() : 0;

      final FEFont font = glyph.getFont();

      final HintingCues hinting_cues = font.getHintingCues();
     
      final int letter_o_bottom = hinting_cues.getBottomLetterO() + v_offset;
      final int letter_o_top = hinting_cues.getTopLetterO() + v_offset;
      final int letter_h_top = hinting_cues.getTopLetterH() + v_offset;

      final int new_letter_o_bottom = gdo.getCoords().nearestY(letter_o_bottom, gdo.getLineWidthOffsetNorth());
      final int new_letter_o_top = gdo.getCoords().nearestY(letter_o_bottom, gdo.getLineWidthOffsetSouth());
      final int new_letter_h_top = gdo.getCoords().nearestY(letter_h_top, gdo.getLineWidthOffsetSouth());

      HintingMapElement h_m_e;
      final HintingMap h_m = new HintingMap();

      h_m_e = new HintingMapElement(0, 0, true);
      h_m.add(h_m_e);
      h_m_e = new HintingMapElement(letter_h_top, new_letter_h_top, true);
      h_m.add(h_m_e);
      h_m_e = new HintingMapElement(letter_o_top, new_letter_o_top, true);
      h_m.add(h_m_e);
      h_m_e = new HintingMapElement(new_letter_o_top, new_letter_o_bottom, false);
      h_m.add(h_m_e);
      h_m_e = new HintingMapElement(0xFFFF, 0xFFFF, false);
      h_m.add(h_m_e);

      //values_old, values_new, is_top);

      glyph.translate(-gdo.getLineWidthOffsetWest(), new_letter_o_bottom - letter_o_bottom + v_offset, gdo);

      hintHorizontally(glyph, gdo, s);
      hintVertically(glyph, gdo, s, h_m);
    }
  }

  private static void hintVertically(FEGlyph glyph, DisplayOptions gdo, Sliders s, HintingMap hinting_map) {
    if (!hint_y) {
      return;
    }

    // Step 1 - hint according to baselines and top lines...

    final SliderManager s_m_h = (SliderManager) s.getSliderManagerHorizontal();
    final int new_letter_o_bottom = hinting_map.getElement(3).getValueNew();
    hintBasicBaseToBottom(glyph, gdo, s_m_h, new_letter_o_bottom);
    hintBasicBaseToTop(glyph, gdo, s_m_h, new_letter_o_bottom);

    // Step 2 - hint according to top and bottom of characters...

    hintWholeBaseToBottom(glyph, gdo, s_m_h);
    hintWholeBaseToTop(glyph, gdo, s_m_h);

    // Hint recursively inside existing regions...

    hintY(glyph, gdo, s_m_h);
  }

  private static void hintHorizontally(FEGlyph glyph, DisplayOptions gdo, Sliders s) {
    if (!hint_x) {
      return;
    }

    final SliderManager s_m_v = (SliderManager) s.getSliderManagerVertical();
    hintBasicRight(glyph, gdo, s_m_v);
    hintBasicLeft(glyph, gdo, s_m_v);

    hintX(glyph, gdo, s_m_v);
  }

  protected static void hintBasicRight(FEGlyph g, DisplayOptions gdo, SliderManager s_m) {
    int min_pos = s_m.getMin();
    int max_pos = s_m.getMax();
    int new_min_pos = gdo.getCoords().nearestX(min_pos, gdo.getLineWidthOffsetWest());
    g.rescaleWithFixedRight(max_pos, min_pos, new_min_pos, gdo);

    //Log.log("min_pos:" + min_pos + " new_min_pos:" + new_min_pos);
  }

  protected static void hintBasicLeft(FEGlyph g, DisplayOptions gdo, SliderManager s_m) {
    int min_pos = s_m.getMin();
    int max_pos = s_m.getMax();
    int new_max_pos = gdo.getCoords().nearestX(max_pos, gdo.getLineWidthOffsetEast());
    g.rescaleWithFixedLeft(min_pos, max_pos, new_max_pos, gdo);
  }

  protected static void hintBasicBaseToBottom(FEGlyph g, DisplayOptions gdo, SliderManager s_m, int base_line) {
    int min_pos = base_line;
    int max_pos = s_m.getMax();
    int new_max_pos = gdo.getCoords().nearestY(max_pos, gdo.getLineWidthOffsetSouth());
    g.rescaleWithFixedTop(min_pos, max_pos, new_max_pos, gdo);
  }

  protected static void hintBasicBaseToTop(FEGlyph g, DisplayOptions gdo, SliderManager s_m, int base_line) {
    int min_pos = s_m.getMin();
    int max_pos = base_line;
    int new_min_pos = gdo.getCoords().nearestY(min_pos, gdo.getLineWidthOffsetNorth());
    g.rescaleWithFixedBottom(max_pos, min_pos, new_min_pos, gdo);
  }

  protected static void hintWholeBaseToBottom(FEGlyph g, DisplayOptions gdo, SliderManager s_m) {
    int min_pos = s_m.getMin();
    int max_pos = s_m.getMax();
    int new_max_pos = gdo.getCoords().nearestY(max_pos, gdo.getLineWidthOffsetSouth());
    g.rescaleWithFixedTop(min_pos, max_pos, new_max_pos, gdo);
  }

  protected static void hintWholeBaseToTop(FEGlyph g, DisplayOptions gdo, SliderManager s_m) {
    int min_pos = s_m.getMin();
    int max_pos = s_m.getMax();
    int new_min_pos = gdo.getCoords().nearestY(min_pos, gdo.getLineWidthOffsetNorth());
    g.rescaleWithFixedBottom(max_pos, min_pos, new_min_pos, gdo);
  }

  protected static void hintX(FEGlyph glyph, DisplayOptions gdo, SliderManager s_m) {
    if (s_m.getNumber() == 0) {
      return;
    }

    PointValueGetter pvg = new PointValueGetter() {
      int getPointValue(FEPoint point) {
        return point.getX();
      }
    };

    NearestValueGetter nvg = new NearestValueGetter() {
      int getNearestValue(DisplayOptions gdo, int v1, Slider s) {
        return gdo.getCoords().nearestX(v1, s.isHomewards() ? gdo.getLineWidthOffsetWest() : gdo.getLineWidthOffsetEast());
      }
    };

    SectionRescaler rescaler = new SectionRescaler() {
      void rescaleSection(int new_pos, FEPointList fepl, FEPoint point, int index_on_path, int min_index, int max_index) {
        int temp_x = point.getX();
        int temp_y = point.getY();
        fepl.scaleRangeX(min_index, index_on_path, new_pos, min_index, index_on_path);

        point.setX(temp_x);
        point.setY(temp_y);
        fepl.scaleRangeX(max_index, index_on_path, new_pos, index_on_path, max_index);
      }
    };

    recursivelyDivide(glyph, gdo, s_m, 0, s_m.getNumber() - 1, pvg, nvg, rescaler);
  }

  protected static void hintY(FEGlyph glyph, DisplayOptions gdo, SliderManager s_m) {
    if (s_m.getNumber() == 0) {
      return;
    }

    PointValueGetter pvg = new PointValueGetter() {
      int getPointValue(FEPoint point) {
        return point.getY();
      }
    };

    NearestValueGetter nvg = new NearestValueGetter() {
      int getNearestValue(DisplayOptions gdo, int v1, Slider s) {
        return gdo.getCoords().nearestY(v1, s.isHomewards() ? gdo.getLineWidthOffsetSouth() : gdo.getLineWidthOffsetNorth());
      }
    };

    SectionRescaler rescaler = new SectionRescaler() {
      void rescaleSection(int new_pos, FEPointList fepl, FEPoint point, int index_on_path, int min_index, int max_index) {
        int temp_x = point.getX();
        int temp_y = point.getY();
        fepl.scaleRangeY(min_index, index_on_path, new_pos, min_index, index_on_path);
        point.setX(temp_x);
        point.setY(temp_y);
        fepl.scaleRangeY(max_index, index_on_path, new_pos, index_on_path, max_index);
      }
    };

    recursivelyDivide(glyph, gdo, s_m, 0, s_m.getNumber() - 1, pvg, nvg, rescaler);
  }

  private static void recursivelyDivide(FEGlyph glyph, DisplayOptions gdo, SliderManager s_m, int min, int max, PointValueGetter pvg, NearestValueGetter nvg, SectionRescaler rescaler) {
    if (max - min < 2) {
      return;
    }

    int idx = (min + max) >> 1;
    Slider s = s_m.getSlider(idx);

    if (s.canMove()) {
      int old_pos = s.getPosition();
      int new_pos = nvg.getNearestValue(gdo, old_pos, s);

      int index_current = -1;

      do {
        FEPointList fepl_of_glyph = glyph.getFEPointList(gdo);
        index_current = getIndexOfNextMatchingPoint(fepl_of_glyph, index_current, old_pos, pvg);
        if (index_current >= 0) {
          FEPath path = glyph.getFEPathList(gdo).getPath(fepl_of_glyph.getPoint(index_current));
          FEPointList fepl = path.getFEPointList();
          FEPoint point = fepl_of_glyph.getPoint(index_current);
          int index_on_path = fepl.getIndexOf(point);
          if (index_on_path >= 0) {

            int min_index = getIndexOfNextSlider(fepl, index_on_path, s_m, -1, pvg);
            int max_index = getIndexOfNextSlider(fepl, index_on_path, s_m, 1, pvg);

            rescaler.rescaleSection(new_pos, fepl, point, index_on_path, min_index, max_index);
          }
        }
      } while (index_current >= 0);
    }

    recursivelyDivide(glyph, gdo, s_m, min, idx, pvg, nvg, rescaler);
    recursivelyDivide(glyph, gdo, s_m, idx, max, pvg, nvg, rescaler);
  }

  private static void rescaleSectionY(int new_pos, FEPointList fepl, FEPoint point, int index_on_path, int min_index, int max_index) {
    int temp_x = point.getX();
    int temp_y = point.getY();
    fepl.scaleRangeY(min_index, index_on_path, new_pos, min_index, index_on_path);
    point.setX(temp_x);
    point.setY(temp_y);
    fepl.scaleRangeY(max_index, index_on_path, new_pos, index_on_path, max_index);
  }

  private static int getIndexOfNextMatchingPoint(FEPointList fepl, int idx, int pos, PointValueGetter pvg) {
    int number = fepl.getNumber();

    if (idx == -1) {
      idx++;
    } else {
      for (; true;) {
        if (++idx >= number) {
          return -1;
        }

        if (pvg.getPointValue(fepl.getPoint(idx)) != pos) {
          break;
        }
      }
    }

    for (; true;) {
      if (pvg.getPointValue(fepl.getPoint(idx)) == pos) {
        return idx;
      }

      if (++idx >= number) {
        return -1;
      }
    }
  }

  private static int getIndexOfNextSlider(FEPointList fepl, int idx, SliderManager s_m, int dir, PointValueGetter pvg) {
    if (Math.abs(dir) != 1) {
      throw new RuntimeException("getIndexOfNextSlider:Looped");
    }

    int looped = idx;
    int pos = pvg.getPointValue(fepl.getPoint(looped));
    int number = fepl.getNumber();

    while (pvg.getPointValue(fepl.getPoint(idx)) == pos) {
      idx += dir;
      if (idx >= number) {
        idx = 0;
      }

      if (idx < 0) {
        idx = number - 1;
      }

      if (idx == looped) {
        return idx;
        // throw new RuntimeException("getIndexOfNextSlider:Looped"); // FIZ
      }
    }

    looped = idx;
    while (!s_m.isSliderAt(pvg.getPointValue(fepl.getPoint(idx)))) {
      idx += dir;
      if (idx >= number) {
        idx = 0;
      }

      if (idx < 0) {
        idx = number - 1;
      }

      if (idx == looped) {
        return idx;
        // throw new RuntimeException("getIndexOfNextSlider:Looped"); // FIZ
      }
    }

    return idx;
  }
}

abstract class PointValueGetter {
  abstract int getPointValue(FEPoint point);
}

abstract class NearestValueGetter {
  abstract int getNearestValue(DisplayOptions gdo, int v1, Slider s);
}

abstract class SectionRescaler {
  abstract void rescaleSection(int new_pos, FEPointList fepl, FEPoint point, int index_on_path, int min_index, int max_index);
}
TOP

Related Classes of org.fonteditor.hinter.SectionRescaler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.