2 * Copyright (c) 2009, Takeyuki Nagao
5 * Redistribution and use in source and binary forms, with or
6 * without modification, are permitted provided that the
7 * following conditions are met:
9 * * Redistributions of source code must retain the above
10 * copyright notice, this list of conditions and the
11 * following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the
14 * following disclaimer in the documentation and/or other
15 * materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
18 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
29 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
33 package jp.sourceforge.dvibrowser.dvicore.font;
35 import java.io.PrintWriter;
36 import java.io.StringWriter;
37 import java.util.ArrayList;
39 import jp.sourceforge.dvibrowser.dvicore.DviException;
40 import jp.sourceforge.dvibrowser.dvicore.DviPoint;
41 import jp.sourceforge.dvibrowser.dvicore.DviRect;
42 import jp.sourceforge.dvibrowser.dvicore.DviResolution;
43 import jp.sourceforge.dvibrowser.dvicore.api.BinaryDevice;
44 import jp.sourceforge.dvibrowser.dvicore.render.AbstractDevice;
49 public final class RunLengthEncodedGlyph
51 private static final int DEFAULT_ARRAY_SIZE = 256;
57 private ArrayList<RunLengthEncodedLine> lines;
59 public RunLengthEncodedGlyph()
64 private RunLengthEncodedGlyph(int width, int height, int xOffset, int yOffset)
68 this.xOffset = xOffset;
69 this.yOffset = yOffset;
70 this.lines = new ArrayList<RunLengthEncodedLine>(DEFAULT_ARRAY_SIZE);
73 public static RunLengthEncodedGlyph readByteBinary(
74 byte [] buf, int width, int height,
75 int xOffset, int yOffset)
77 RunLengthEncodedGlyph rlg = new RunLengthEncodedGlyph(width, height, xOffset, yOffset);
79 RunLengthEncodedLine prev = null;
80 int pitch = ((width + 7) >>> 3) << 3;
82 for (int i=0; i<height; i++) {
83 RunLengthEncodedLine line = new RunLengthEncodedLine();
84 line.append(buf, bitOffset, width);
85 if (line.equals(prev)) {
99 public static RunLengthEncodedGlyph readRasterByBits(
100 byte [] buf, int width, int height,
101 int xOffset, int yOffset)
103 RunLengthEncodedGlyph rlg = new RunLengthEncodedGlyph(width, height, xOffset, yOffset);
106 RunLengthEncodedLine prev = null;
107 for (int i=0; i<height; i++) {
108 RunLengthEncodedLine line = new RunLengthEncodedLine();
109 line.append(buf, bitOffset, width);
111 if (line.equals(prev)) {
123 public static RunLengthEncodedGlyph readByteGray(
124 byte [] buf, int width, int height,
125 int xOffset, int yOffset)
127 RunLengthEncodedGlyph rlg = new RunLengthEncodedGlyph(width, height, xOffset, yOffset);
130 RunLengthEncodedLine prev = null;
131 for (int i=0; i<height; i++) {
132 final RunLengthEncodedLine line = new RunLengthEncodedLine();
134 int last = 314; // A MAGIC value to denote the begin of line.
136 for (int j=0; j<width; j++) {
137 final int c = buf[ptr++];
142 line.append(count, (0 != last));
148 line.append(count, (0 != last));
150 if (line.equals(prev)) {
163 public DviRect getBounds()
165 return new DviRect(-xOffset, -yOffset, width, height);
167 public int width() { return width; }
168 public int height() { return height; }
169 public int xOffset() { return xOffset; }
170 public int yOffset() { return yOffset; }
172 public boolean isEmpty()
174 return (width <= 0 || height <= 0 || lines.size() == 0);
177 public void rasterizeTo(BinaryDevice out)
182 out.translate(-xOffset, -yOffset);
183 if (out.beginRaster(width, height)) {
185 for (int i=0; i<height; i++) {
186 RunLengthEncodedLine line = lines.get(i);
188 line.rasterizeTo(out);
198 public void compact()
200 int lskip = Integer.MAX_VALUE;
201 int rskip = Integer.MAX_VALUE;
203 RunLengthEncodedLine prev = null;
204 for (int i=0; i<height; i++) {
205 RunLengthEncodedLine line = lines.get(i);
206 if (prev == line) continue;
211 lskip = Math.min(lskip, line.head());
217 for (int i=0; i<height; i++) {
218 RunLengthEncodedLine line = lines.get(i);
219 if (prev == line) continue;
220 line.cropHead(lskip);
229 xOffset = yOffset = 0;
234 for (int i=0; i<height; i++) {
235 RunLengthEncodedLine line = lines.get(i);
236 if (prev == line) continue;
241 rskip = Math.min(rskip, line.tail());
248 for (int i=0; i<height; i++) {
249 RunLengthEncodedLine line = lines.get(i);
250 if (prev == line) continue;
251 line.cropTail(rskip);
259 xOffset = yOffset = 0;
267 RunLengthEncodedLine line = lines.get(0);
277 RunLengthEncodedLine line = lines.get(height-1);
280 lines.remove(height-1);
285 for (int i=0; i<height; i++) {
286 RunLengthEncodedLine line = lines.get(i);
287 if (line.isEmpty()) {
288 throw new IllegalStateException
289 ("width=" + width + " height=" + height);
292 if (width <= 0 || height <= 0) {
293 throw new IllegalStateException
294 ("width=" + width + " height=" + height);
300 StringWriter sw = new StringWriter();
301 PrintWriter pw = new PrintWriter(sw);
302 for (int i=0; i<height; i++) {
303 RunLengthEncodedLine line = lines.get(i);
304 boolean duplicate = (
305 (i > 0) && lines.get(i-1) == line
307 pw.println((duplicate ? "+ " : " " ) + line.dump());
309 return sw.toString();
312 public String toString()
314 return getClass().getName()
320 public PkGlyph toPkGlyph()
323 return PkGlyph.EMPTY;
325 ArrayList<Integer> counts = new ArrayList<Integer>(DEFAULT_ARRAY_SIZE);
329 RunLengthEncodedLine line = lines.get(0);
330 boolean turnOn = line.headOn();
332 while (line != null) {
333 ArrayList<Integer> data = line.getData();
334 final int ds = data.size();
335 for (int j=0; j<ds; j++) {
337 boolean needFlush = true;
339 // at the end of the line.
341 RunLengthEncodedLine next = null;
342 while (++i < height) {
350 next.headOn() != line.tailOn()
354 count += data.get(j) + r * width;
355 // repeat is unchanged.
374 PackedSequence ps = new SequencePacker(counts).pack();
378 ps.data(), ps.dynF(),
384 public DviRect bounds()
388 return DviRect.EMPTY;
390 return new DviRect(-xOffset, -yOffset, width, height);
393 public void unite(RunLengthEncodedGlyph rlg)
395 DviRect u = rlg.getBounds().union(getBounds());
397 ArrayList<RunLengthEncodedLine> newLines
398 = new ArrayList<RunLengthEncodedLine>(DEFAULT_ARRAY_SIZE);
399 RunLengthEncodedLine last = null;
401 for (int y = u.top(); y<=ey; y++) {
402 RunLengthEncodedLine a = new RunLengthEncodedLine();
403 RunLengthEncodedLine b = new RunLengthEncodedLine();
404 int ia = y + yOffset;
405 int ib = y + rlg.yOffset;
407 if (0 <= ia && ia < height) {
409 int lpad = -xOffset-u.left();
411 a.append(lpad, false);
414 a.append(lines.get(ia));
418 a.append(u.width(), false);
421 if (0 <= ib && ib < rlg.height) {
423 int lpad = -rlg.xOffset-u.left();
425 b.append(lpad, false);
428 b.append(rlg.lines.get(ib));
432 b.append(u.width(), false);
435 a = RunLengthEncodedLine.union(a, b);
437 if (a.equals(last)) {
445 this.width = u.width();
446 this.height = u.height();
447 this.xOffset = -u.x();
448 this.yOffset = -u.y();
449 this.lines = newLines;
454 public BinaryDevice getBinaryDevice(DviResolution res)
457 return new BinaryDeviceImpl(res);
460 // private static String dumpCounts(ArrayList<Integer> counts, boolean flag)
464 // for (int k=0; k<counts.size(); k++) {
465 // int c = counts.get(k);
468 // str += "[" + (-c) + "]";
469 // } else if (flag) {
470 // str += String.valueOf(c);
473 // str += "(" + c + ")";
481 private class BinaryDeviceImpl
482 extends AbstractDevice
483 implements BinaryDevice
485 private BinaryDeviceImpl(DviResolution res)
500 private RunLengthEncodedGlyph rlg = null;
501 public boolean beginRaster(int w, int h)
504 rlg = new RunLengthEncodedGlyph();
510 public void endRaster()
513 DviPoint p = getReferencePoint();
514 // The reference point of rlg is at the origin of this device.
523 private RunLengthEncodedLine line = null;
524 public void beginLine()
527 line = new RunLengthEncodedLine();
530 public void endLine(int repeat)
533 for (int i=0; i<=repeat; i++) {
539 public void putBits(int count, boolean paintFlag)
541 line.append(count, paintFlag);