├── .idea
├── .name
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── vcs.xml
├── modules.xml
├── libraries
│ ├── glide_3_7_0.xml
│ ├── support_annotations_23_0_0.xml
│ ├── appcompat_v7_23_0_0.xml
│ └── support_v4_23_0_0.xml
├── runConfigurations.xml
├── compiler.xml
├── gradle.xml
└── misc.xml
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── raw
│ │ │ │ └── kof_13.mp4
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── dimens.xml
│ │ │ ├── menu
│ │ │ │ └── menu_main.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── beak
│ │ │ └── gifmaker
│ │ │ ├── MainActivity.java
│ │ │ └── GifMakeService.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── beak
│ │ └── gifmaker
│ │ └── ApplicationTest.java
├── proguard-rules.pro
├── build.gradle
└── app.iml
├── gifmakerlib
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── beak
│ │ │ └── gifmakerlib
│ │ │ ├── GifMaker.java
│ │ │ └── AnimatedGifEncoder.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── beak
│ │ └── gifmakerlib
│ │ └── ApplicationTest.java
├── build.gradle
├── proguard-rules.pro
└── gifmakerlib.iml
├── settings.gradle
├── demo_pic.png
├── make_this_gif.gif
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitattributes
├── readme.md
├── gradle.properties
├── GifMaker.iml
├── .gitignore
├── gradlew.bat
└── gradlew
/.idea/.name:
--------------------------------------------------------------------------------
1 | GifMaker
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/gifmakerlib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':gifmakerlib'
2 |
--------------------------------------------------------------------------------
/demo_pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boybeak/GifMaker/HEAD/demo_pic.png
--------------------------------------------------------------------------------
/make_this_gif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boybeak/GifMaker/HEAD/make_this_gif.gif
--------------------------------------------------------------------------------
/app/src/main/res/raw/kof_13.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boybeak/GifMaker/HEAD/app/src/main/res/raw/kof_13.mp4
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/boybeak/GifMaker/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
`
12 |
13 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 | finish() flushes all frames. If
115 | * setSize was not invoked, the size of the first image is used
116 | * for all subsequent frames.
117 | *
118 | * @param im
119 | * BufferedImage containing frame to write.
120 | * @return true if successful.
121 | */
122 | public boolean addFrame(Bitmap im) {
123 | if ((im == null) || !started) {
124 | return false;
125 | }
126 | boolean ok = true;
127 | try {
128 | if (!sizeSet) {
129 | // use first frame's size
130 | setSize(im.getWidth(), im.getHeight());
131 | }
132 | image = im;
133 | getImagePixels(); // convert to correct format if necessary
134 | analyzePixels(); // build color table & map pixels
135 | if (firstFrame) {
136 | writeLSD(); // logical screen descriptior
137 | writePalette(); // global color table
138 | if (repeat >= 0) {
139 | // use NS app extension to indicate reps
140 | writeNetscapeExt();
141 | }
142 | }
143 | writeGraphicCtrlExt(); // write graphic control extension
144 | writeImageDesc(); // image descriptor
145 | if (!firstFrame) {
146 | writePalette(); // local color table
147 | }
148 | writePixels(); // encode and write pixel data
149 | firstFrame = false;
150 | } catch (IOException e) {
151 | ok = false;
152 | }
153 |
154 | return ok;
155 | }
156 |
157 | /**
158 | * Flushes any pending data and closes output file. If writing to an
159 | * OutputStream, the stream is not closed.
160 | */
161 | public boolean finish() {
162 | if (!started)
163 | return false;
164 | boolean ok = true;
165 | started = false;
166 | try {
167 | out.write(0x3b); // gif trailer
168 | out.flush();
169 | if (closeStream) {
170 | out.close();
171 | }
172 | } catch (IOException e) {
173 | ok = false;
174 | }
175 |
176 | // reset for subsequent use
177 | transIndex = 0;
178 | out = null;
179 | image = null;
180 | pixels = null;
181 | indexedPixels = null;
182 | colorTab = null;
183 | closeStream = false;
184 | firstFrame = true;
185 |
186 | return ok;
187 | }
188 |
189 | /**
190 | * Sets frame rate in frames per second. Equivalent to
191 | * setDelay(1000/fps).
192 | *
193 | * @param fps
194 | * float frame rate (frames per second)
195 | */
196 | public void setFrameRate(float fps) {
197 | if (fps != 0f) {
198 | delay = (int)(100 / fps);
199 | }
200 | }
201 |
202 | /**
203 | * Sets quality of color quantization (conversion of images to the maximum 256
204 | * colors allowed by the GIF specification). Lower values (minimum = 1)
205 | * produce better colors, but slow processing significantly. 10 is the
206 | * default, and produces good color mapping at reasonable speeds. Values
207 | * greater than 20 do not yield significant improvements in speed.
208 | *
209 | * @param quality
210 | * int greater than 0.
211 | * @return
212 | */
213 | public void setQuality(int quality) {
214 | if (quality < 1)
215 | quality = 1;
216 | sample = quality;
217 | }
218 |
219 | /**
220 | * Sets the GIF frame size. The default size is the size of the first frame
221 | * added if this method is not invoked.
222 | *
223 | * @param w
224 | * int frame width.
225 | * @param h
226 | * int frame width.
227 | */
228 | public void setSize(int w, int h) {
229 | width = w;
230 | height = h;
231 | if (width < 1)
232 | width = 320;
233 | if (height < 1)
234 | height = 240;
235 | sizeSet = true;
236 | }
237 |
238 | /**
239 | * Sets the GIF frame position. The position is 0,0 by default.
240 | * Useful for only updating a section of the image
241 | *
242 | * @param w
243 | * int frame width.
244 | * @param h
245 | * int frame width.
246 | */
247 | public void setPosition(int x, int y) {
248 | this.x = x;
249 | this.y = y;
250 | }
251 |
252 | /**
253 | * Initiates GIF file creation on the given stream. The stream is not closed
254 | * automatically.
255 | *
256 | * @param os
257 | * OutputStream on which GIF images are written.
258 | * @return false if initial write failed.
259 | */
260 | public boolean start(OutputStream os) {
261 | if (os == null)
262 | return false;
263 | boolean ok = true;
264 | closeStream = false;
265 | out = os;
266 | try {
267 | writeString("GIF89a"); // header
268 | } catch (IOException e) {
269 | ok = false;
270 | }
271 | return started = ok;
272 | }
273 |
274 | /**
275 | * Analyzes image colors and creates color map.
276 | */
277 | protected void analyzePixels() {
278 | int len = pixels.length;
279 | int nPix = len / 3;
280 | indexedPixels = new byte[nPix];
281 | NeuQuant nq = new NeuQuant(pixels, len, sample);
282 | // initialize quantizer
283 | colorTab = nq.process(); // create reduced palette
284 | // convert map from BGR to RGB
285 | for (int i = 0; i < colorTab.length; i += 3) {
286 | byte temp = colorTab[i];
287 | colorTab[i] = colorTab[i + 2];
288 | colorTab[i + 2] = temp;
289 | usedEntry[i / 3] = false;
290 | }
291 | // map image pixels to new palette
292 | int k = 0;
293 | for (int i = 0; i < nPix; i++) {
294 | int index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff);
295 | usedEntry[index] = true;
296 | indexedPixels[i] = (byte) index;
297 | }
298 | pixels = null;
299 | colorDepth = 8;
300 | palSize = 7;
301 | // get closest match to transparent color if specified
302 | if (transparent != -1) {
303 | transIndex = findClosest(transparent);
304 | }
305 | }
306 |
307 | /**
308 | * Returns index of palette color closest to c
309 | *
310 | */
311 | protected int findClosest(int c) {
312 | if (colorTab == null)
313 | return -1;
314 | int r = (c >> 16) & 0xff;
315 | int g = (c >> 8) & 0xff;
316 | int b = (c >> 0) & 0xff;
317 | int minpos = 0;
318 | int dmin = 256 * 256 * 256;
319 | int len = colorTab.length;
320 | for (int i = 0; i < len;) {
321 | int dr = r - (colorTab[i++] & 0xff);
322 | int dg = g - (colorTab[i++] & 0xff);
323 | int db = b - (colorTab[i] & 0xff);
324 | int d = dr * dr + dg * dg + db * db;
325 | int index = i / 3;
326 | if (usedEntry[index] && (d < dmin)) {
327 | dmin = d;
328 | minpos = index;
329 | }
330 | i++;
331 | }
332 | return minpos;
333 | }
334 |
335 | /**
336 | * Extracts image pixels into byte array "pixels"
337 | */
338 | protected void getImagePixels() {
339 | int w = image.getWidth();
340 | int h = image.getHeight();
341 | if ((w != width) || (h != height)) {
342 | // create new image with right size/format
343 | Bitmap temp = Bitmap.createBitmap(width, height, Config.RGB_565);
344 | Canvas g = new Canvas(temp);
345 | g.drawBitmap(image, 0, 0, new Paint());
346 | image = temp;
347 | }
348 | int[] data = getImageData(image);
349 | pixels = new byte[data.length * 3];
350 | for (int i = 0; i < data.length; i++) {
351 | int td = data[i];
352 | int tind = i * 3;
353 | pixels[tind++] = (byte) ((td >> 0) & 0xFF);
354 | pixels[tind++] = (byte) ((td >> 8) & 0xFF);
355 | pixels[tind] = (byte) ((td >> 16) & 0xFF);
356 | }
357 | }
358 | protected int[] getImageData(Bitmap img) {
359 | int w = img.getWidth();
360 | int h = img.getHeight();
361 |
362 | int[] data = new int[w * h];
363 | img.getPixels(data, 0, w, 0, 0, w, h);
364 | return data;
365 | }
366 |
367 | /**
368 | * Writes Graphic Control Extension
369 | */
370 | protected void writeGraphicCtrlExt() throws IOException {
371 | out.write(0x21); // extension introducer
372 | out.write(0xf9); // GCE label
373 | out.write(4); // data block size
374 | int transp, disp;
375 | if (transparent == -1) {
376 | transp = 0;
377 | disp = 0; // dispose = no action
378 | } else {
379 | transp = 1;
380 | disp = 2; // force clear if using transparent color
381 | }
382 | if (dispose >= 0) {
383 | disp = dispose & 7; // user override
384 | }
385 | disp <<= 2;
386 |
387 | // packed fields
388 | out.write(0 | // 1:3 reserved
389 | disp | // 4:6 disposal
390 | 0 | // 7 user input - 0 = none
391 | transp); // 8 transparency flag
392 |
393 | writeShort(delay); // delay x 1/100 sec
394 | out.write(transIndex); // transparent color index
395 | out.write(0); // block terminator
396 | }
397 |
398 | /**
399 | * Writes Image Descriptor
400 | */
401 | protected void writeImageDesc() throws IOException {
402 | out.write(0x2c); // image separator
403 | writeShort(x); // image position x,y = 0,0
404 | writeShort(y);
405 | writeShort(width); // image size
406 | writeShort(height);
407 | // packed fields
408 | if (firstFrame) {
409 | // no LCT - GCT is used for first (or only) frame
410 | out.write(0);
411 | } else {
412 | // specify normal LCT
413 | out.write(0x80 | // 1 local color table 1=yes
414 | 0 | // 2 interlace - 0=no
415 | 0 | // 3 sorted - 0=no
416 | 0 | // 4-5 reserved
417 | palSize); // 6-8 size of color table
418 | }
419 | }
420 |
421 | /**
422 | * Writes Logical Screen Descriptor
423 | */
424 | protected void writeLSD() throws IOException {
425 | // logical screen size
426 | writeShort(width);
427 | writeShort(height);
428 | // packed fields
429 | out.write((0x80 | // 1 : global color table flag = 1 (gct used)
430 | 0x70 | // 2-4 : color resolution = 7
431 | 0x00 | // 5 : gct sort flag = 0
432 | palSize)); // 6-8 : gct size
433 |
434 | out.write(0); // background color index
435 | out.write(0); // pixel aspect ratio - assume 1:1
436 | }
437 |
438 | /**
439 | * Writes Netscape application extension to define repeat count.
440 | */
441 | protected void writeNetscapeExt() throws IOException {
442 | out.write(0x21); // extension introducer
443 | out.write(0xff); // app extension label
444 | out.write(11); // block size
445 | writeString("NETSCAPE" + "2.0"); // app id + auth code
446 | out.write(3); // sub-block size
447 | out.write(1); // loop sub-block id
448 | writeShort(repeat); // loop count (extra iterations, 0=repeat forever)
449 | out.write(0); // block terminator
450 | }
451 |
452 | /**
453 | * Writes color table
454 | */
455 | protected void writePalette() throws IOException {
456 | out.write(colorTab, 0, colorTab.length);
457 | int n = (3 * 256) - colorTab.length;
458 | for (int i = 0; i < n; i++) {
459 | out.write(0);
460 | }
461 | }
462 |
463 | /**
464 | * Encodes and writes pixel data
465 | */
466 | protected void writePixels() throws IOException {
467 | LZWEncoder encoder = new LZWEncoder(width, height, indexedPixels, colorDepth);
468 | encoder.encode(out);
469 | }
470 |
471 | /**
472 | * Write 16-bit value to output stream, LSB first
473 | */
474 | protected void writeShort(int value) throws IOException {
475 | out.write(value & 0xff);
476 | out.write((value >> 8) & 0xff);
477 | }
478 |
479 | /**
480 | * Writes string to output stream
481 | */
482 | protected void writeString(String s) throws IOException {
483 | for (int i = 0; i < s.length(); i++) {
484 | out.write((byte) s.charAt(i));
485 | }
486 | }
487 | }
488 |
489 | /*
490 | * NeuQuant Neural-Net Quantization Algorithm
491 | * ------------------------------------------
492 | *
493 | * Copyright (c) 1994 Anthony Dekker
494 | *
495 | * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
496 | * "Kohonen neural networks for optimal colour quantization" in "Network:
497 | * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
498 | * the algorithm.
499 | *
500 | * Any party obtaining a copy of these files from the author, directly or
501 | * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
502 | * world-wide, paid up, royalty-free, nonexclusive right and license to deal in
503 | * this software and documentation files (the "Software"), including without
504 | * limitation the rights to use, copy, modify, merge, publish, distribute,
505 | * sublicense, and/or sell copies of the Software, and to permit persons who
506 | * receive copies from any such party to do so, with the only requirement being
507 | * that this copyright notice remain intact.
508 | */
509 |
510 | // Ported to Java 12/00 K Weiner
511 | class NeuQuant {
512 |
513 | protected static final int netsize = 256; /* number of colours used */
514 |
515 | /* four primes near 500 - assume no image has a length so large */
516 | /* that it is divisible by all four primes */
517 | protected static final int prime1 = 499;
518 |
519 | protected static final int prime2 = 491;
520 |
521 | protected static final int prime3 = 487;
522 |
523 | protected static final int prime4 = 503;
524 |
525 | protected static final int minpicturebytes = (3 * prime4);
526 |
527 | /* minimum size for input image */
528 |
529 | /*
530 | * Program Skeleton ---------------- [select samplefac in range 1..30] [read
531 | * image from input file] pic = (unsigned char*) malloc(3*width*height);
532 | * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output
533 | * image header, using writecolourmap(f)] inxbuild(); write output image using
534 | * inxsearch(b,g,r)
535 | */
536 |
537 | /*
538 | * Network Definitions -------------------
539 | */
540 |
541 | protected static final int maxnetpos = (netsize - 1);
542 |
543 | protected static final int netbiasshift = 4; /* bias for colour values */
544 |
545 | protected static final int ncycles = 100; /* no. of learning cycles */
546 |
547 | /* defs for freq and bias */
548 | protected static final int intbiasshift = 16; /* bias for fractions */
549 |
550 | protected static final int intbias = (((int) 1) << intbiasshift);
551 |
552 | protected static final int gammashift = 10; /* gamma = 1024 */
553 |
554 | protected static final int gamma = (((int) 1) << gammashift);
555 |
556 | protected static final int betashift = 10;
557 |
558 | protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */
559 |
560 | protected static final int betagamma = (intbias << (gammashift - betashift));
561 |
562 | /* defs for decreasing radius factor */
563 | protected static final int initrad = (netsize >> 3); /*
564 | * for 256 cols, radius
565 | * starts
566 | */
567 |
568 | protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */
569 |
570 | protected static final int radiusbias = (((int) 1) << radiusbiasshift);
571 |
572 | protected static final int initradius = (initrad * radiusbias); /*
573 | * and
574 | * decreases
575 | * by a
576 | */
577 |
578 | protected static final int radiusdec = 30; /* factor of 1/30 each cycle */
579 |
580 | /* defs for decreasing alpha factor */
581 | protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */
582 |
583 | protected static final int initalpha = (((int) 1) << alphabiasshift);
584 |
585 | protected int alphadec; /* biased by 10 bits */
586 |
587 | /* radbias and alpharadbias used for radpower calculation */
588 | protected static final int radbiasshift = 8;
589 |
590 | protected static final int radbias = (((int) 1) << radbiasshift);
591 |
592 | protected static final int alpharadbshift = (alphabiasshift + radbiasshift);
593 |
594 | protected static final int alpharadbias = (((int) 1) << alpharadbshift);
595 |
596 | /*
597 | * Types and Global Variables --------------------------
598 | */
599 |
600 | protected byte[] thepicture; /* the input image itself */
601 |
602 | protected int lengthcount; /* lengthcount = H*W*3 */
603 |
604 | protected int samplefac; /* sampling factor 1..30 */
605 |
606 | // typedef int pixel[4]; /* BGRc */
607 | protected int[][] network; /* the network itself - [netsize][4] */
608 |
609 | protected int[] netindex = new int[256];
610 |
611 | /* for network lookup - really 256 */
612 |
613 | protected int[] bias = new int[netsize];
614 |
615 | /* bias and freq arrays for learning */
616 | protected int[] freq = new int[netsize];
617 |
618 | protected int[] radpower = new int[initrad];
619 |
620 | /* radpower for precomputation */
621 |
622 | /*
623 | * Initialise network in range (0,0,0) to (255,255,255) and set parameters
624 | * -----------------------------------------------------------------------
625 | */
626 | public NeuQuant(byte[] thepic, int len, int sample) {
627 |
628 | int i;
629 | int[] p;
630 |
631 | thepicture = thepic;
632 | lengthcount = len;
633 | samplefac = sample;
634 |
635 | network = new int[netsize][];
636 | for (i = 0; i < netsize; i++) {
637 | network[i] = new int[4];
638 | p = network[i];
639 | p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;
640 | freq[i] = intbias / netsize; /* 1/netsize */
641 | bias[i] = 0;
642 | }
643 | }
644 |
645 | public byte[] colorMap() {
646 | byte[] map = new byte[3 * netsize];
647 | int[] index = new int[netsize];
648 | for (int i = 0; i < netsize; i++)
649 | index[network[i][3]] = i;
650 | int k = 0;
651 | for (int i = 0; i < netsize; i++) {
652 | int j = index[i];
653 | map[k++] = (byte) (network[j][0]);
654 | map[k++] = (byte) (network[j][1]);
655 | map[k++] = (byte) (network[j][2]);
656 | }
657 | return map;
658 | }
659 |
660 | /*
661 | * Insertion sort of network and building of netindex[0..255] (to do after
662 | * unbias)
663 | * -------------------------------------------------------------------------------
664 | */
665 | public void inxbuild() {
666 |
667 | int i, j, smallpos, smallval;
668 | int[] p;
669 | int[] q;
670 | int previouscol, startpos;
671 |
672 | previouscol = 0;
673 | startpos = 0;
674 | for (i = 0; i < netsize; i++) {
675 | p = network[i];
676 | smallpos = i;
677 | smallval = p[1]; /* index on g */
678 | /* find smallest in i..netsize-1 */
679 | for (j = i + 1; j < netsize; j++) {
680 | q = network[j];
681 | if (q[1] < smallval) { /* index on g */
682 | smallpos = j;
683 | smallval = q[1]; /* index on g */
684 | }
685 | }
686 | q = network[smallpos];
687 | /* swap p (i) and q (smallpos) entries */
688 | if (i != smallpos) {
689 | j = q[0];
690 | q[0] = p[0];
691 | p[0] = j;
692 | j = q[1];
693 | q[1] = p[1];
694 | p[1] = j;
695 | j = q[2];
696 | q[2] = p[2];
697 | p[2] = j;
698 | j = q[3];
699 | q[3] = p[3];
700 | p[3] = j;
701 | }
702 | /* smallval entry is now in position i */
703 | if (smallval != previouscol) {
704 | netindex[previouscol] = (startpos + i) >> 1;
705 | for (j = previouscol + 1; j < smallval; j++)
706 | netindex[j] = i;
707 | previouscol = smallval;
708 | startpos = i;
709 | }
710 | }
711 | netindex[previouscol] = (startpos + maxnetpos) >> 1;
712 | for (j = previouscol + 1; j < 256; j++)
713 | netindex[j] = maxnetpos; /* really 256 */
714 | }
715 |
716 | /*
717 | * Main Learning Loop ------------------
718 | */
719 | public void learn() {
720 |
721 | int i, j, b, g, r;
722 | int radius, rad, alpha, step, delta, samplepixels;
723 | byte[] p;
724 | int pix, lim;
725 |
726 | if (lengthcount < minpicturebytes)
727 | samplefac = 1;
728 | alphadec = 30 + ((samplefac - 1) / 3);
729 | p = thepicture;
730 | pix = 0;
731 | lim = lengthcount;
732 | samplepixels = lengthcount / (3 * samplefac);
733 | delta = samplepixels / ncycles;
734 | alpha = initalpha;
735 | radius = initradius;
736 |
737 | rad = radius >> radiusbiasshift;
738 | if (rad <= 1)
739 | rad = 0;
740 | for (i = 0; i < rad; i++)
741 | radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
742 |
743 | // fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad);
744 |
745 | if (lengthcount < minpicturebytes)
746 | step = 3;
747 | else if ((lengthcount % prime1) != 0)
748 | step = 3 * prime1;
749 | else {
750 | if ((lengthcount % prime2) != 0)
751 | step = 3 * prime2;
752 | else {
753 | if ((lengthcount % prime3) != 0)
754 | step = 3 * prime3;
755 | else
756 | step = 3 * prime4;
757 | }
758 | }
759 |
760 | i = 0;
761 | while (i < samplepixels) {
762 | b = (p[pix + 0] & 0xff) << netbiasshift;
763 | g = (p[pix + 1] & 0xff) << netbiasshift;
764 | r = (p[pix + 2] & 0xff) << netbiasshift;
765 | j = contest(b, g, r);
766 |
767 | altersingle(alpha, j, b, g, r);
768 | if (rad != 0)
769 | alterneigh(rad, j, b, g, r); /* alter neighbours */
770 |
771 | pix += step;
772 | if (pix >= lim)
773 | pix -= lengthcount;
774 |
775 | i++;
776 | if (delta == 0)
777 | delta = 1;
778 | if (i % delta == 0) {
779 | alpha -= alpha / alphadec;
780 | radius -= radius / radiusdec;
781 | rad = radius >> radiusbiasshift;
782 | if (rad <= 1)
783 | rad = 0;
784 | for (j = 0; j < rad; j++)
785 | radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
786 | }
787 | }
788 | // fprintf(stderr,"finished 1D learning: final alpha=%f
789 | // !\n",((float)alpha)/initalpha);
790 | }
791 |
792 | /*
793 | * Search for BGR values 0..255 (after net is unbiased) and return colour
794 | * index
795 | * ----------------------------------------------------------------------------
796 | */
797 | public int map(int b, int g, int r) {
798 |
799 | int i, j, dist, a, bestd;
800 | int[] p;
801 | int best;
802 |
803 | bestd = 1000; /* biggest possible dist is 256*3 */
804 | best = -1;
805 | i = netindex[g]; /* index on g */
806 | j = i - 1; /* start at netindex[g] and work outwards */
807 |
808 | while ((i < netsize) || (j >= 0)) {
809 | if (i < netsize) {
810 | p = network[i];
811 | dist = p[1] - g; /* inx key */
812 | if (dist >= bestd)
813 | i = netsize; /* stop iter */
814 | else {
815 | i++;
816 | if (dist < 0)
817 | dist = -dist;
818 | a = p[0] - b;
819 | if (a < 0)
820 | a = -a;
821 | dist += a;
822 | if (dist < bestd) {
823 | a = p[2] - r;
824 | if (a < 0)
825 | a = -a;
826 | dist += a;
827 | if (dist < bestd) {
828 | bestd = dist;
829 | best = p[3];
830 | }
831 | }
832 | }
833 | }
834 | if (j >= 0) {
835 | p = network[j];
836 | dist = g - p[1]; /* inx key - reverse dif */
837 | if (dist >= bestd)
838 | j = -1; /* stop iter */
839 | else {
840 | j--;
841 | if (dist < 0)
842 | dist = -dist;
843 | a = p[0] - b;
844 | if (a < 0)
845 | a = -a;
846 | dist += a;
847 | if (dist < bestd) {
848 | a = p[2] - r;
849 | if (a < 0)
850 | a = -a;
851 | dist += a;
852 | if (dist < bestd) {
853 | bestd = dist;
854 | best = p[3];
855 | }
856 | }
857 | }
858 | }
859 | }
860 | return (best);
861 | }
862 |
863 | public byte[] process() {
864 | learn();
865 | unbiasnet();
866 | inxbuild();
867 | return colorMap();
868 | }
869 |
870 | /*
871 | * Unbias network to give byte values 0..255 and record position i to prepare
872 | * for sort
873 | * -----------------------------------------------------------------------------------
874 | */
875 | public void unbiasnet() {
876 |
877 | int i;
878 |
879 | for (i = 0; i < netsize; i++) {
880 | network[i][0] >>= netbiasshift;
881 | network[i][1] >>= netbiasshift;
882 | network[i][2] >>= netbiasshift;
883 | network[i][3] = i; /* record colour no */
884 | }
885 | }
886 |
887 | /*
888 | * Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in
889 | * radpower[|i-j|]
890 | * ---------------------------------------------------------------------------------
891 | */
892 | protected void alterneigh(int rad, int i, int b, int g, int r) {
893 |
894 | int j, k, lo, hi, a, m;
895 | int[] p;
896 |
897 | lo = i - rad;
898 | if (lo < -1)
899 | lo = -1;
900 | hi = i + rad;
901 | if (hi > netsize)
902 | hi = netsize;
903 |
904 | j = i + 1;
905 | k = i - 1;
906 | m = 1;
907 | while ((j < hi) || (k > lo)) {
908 | a = radpower[m++];
909 | if (j < hi) {
910 | p = network[j++];
911 | try {
912 | p[0] -= (a * (p[0] - b)) / alpharadbias;
913 | p[1] -= (a * (p[1] - g)) / alpharadbias;
914 | p[2] -= (a * (p[2] - r)) / alpharadbias;
915 | } catch (Exception e) {
916 | } // prevents 1.3 miscompilation
917 | }
918 | if (k > lo) {
919 | p = network[k--];
920 | try {
921 | p[0] -= (a * (p[0] - b)) / alpharadbias;
922 | p[1] -= (a * (p[1] - g)) / alpharadbias;
923 | p[2] -= (a * (p[2] - r)) / alpharadbias;
924 | } catch (Exception e) {
925 | }
926 | }
927 | }
928 | }
929 |
930 | /*
931 | * Move neuron i towards biased (b,g,r) by factor alpha
932 | * ----------------------------------------------------
933 | */
934 | protected void altersingle(int alpha, int i, int b, int g, int r) {
935 |
936 | /* alter hit neuron */
937 | int[] n = network[i];
938 | n[0] -= (alpha * (n[0] - b)) / initalpha;
939 | n[1] -= (alpha * (n[1] - g)) / initalpha;
940 | n[2] -= (alpha * (n[2] - r)) / initalpha;
941 | }
942 |
943 | /*
944 | * Search for biased BGR values ----------------------------
945 | */
946 | protected int contest(int b, int g, int r) {
947 |
948 | /* finds closest neuron (min dist) and updates freq */
949 | /* finds best neuron (min dist-bias) and returns position */
950 | /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */
951 | /* bias[i] = gamma*((1/netsize)-freq[i]) */
952 |
953 | int i, dist, a, biasdist, betafreq;
954 | int bestpos, bestbiaspos, bestd, bestbiasd;
955 | int[] n;
956 |
957 | bestd = ~(((int) 1) << 31);
958 | bestbiasd = bestd;
959 | bestpos = -1;
960 | bestbiaspos = bestpos;
961 |
962 | for (i = 0; i < netsize; i++) {
963 | n = network[i];
964 | dist = n[0] - b;
965 | if (dist < 0)
966 | dist = -dist;
967 | a = n[1] - g;
968 | if (a < 0)
969 | a = -a;
970 | dist += a;
971 | a = n[2] - r;
972 | if (a < 0)
973 | a = -a;
974 | dist += a;
975 | if (dist < bestd) {
976 | bestd = dist;
977 | bestpos = i;
978 | }
979 | biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
980 | if (biasdist < bestbiasd) {
981 | bestbiasd = biasdist;
982 | bestbiaspos = i;
983 | }
984 | betafreq = (freq[i] >> betashift);
985 | freq[i] -= betafreq;
986 | bias[i] += (betafreq << gammashift);
987 | }
988 | freq[bestpos] += beta;
989 | bias[bestpos] -= betagamma;
990 | return (bestbiaspos);
991 | }
992 | }
993 |
994 | // ==============================================================================
995 | // Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
996 | // K Weiner 12/00
997 |
998 | class LZWEncoder {
999 |
1000 | private static final int EOF = -1;
1001 |
1002 | private int imgW, imgH;
1003 |
1004 | private byte[] pixAry;
1005 |
1006 | private int initCodeSize;
1007 |
1008 | private int remaining;
1009 |
1010 | private int curPixel;
1011 |
1012 | // GIFCOMPR.C - GIF Image compression routines
1013 | //
1014 | // Lempel-Ziv compression based on 'compress'. GIF modifications by
1015 | // David Rowley (mgardi@watdcsu.waterloo.edu)
1016 |
1017 | // General DEFINEs
1018 |
1019 | static final int BITS = 12;
1020 |
1021 | static final int HSIZE = 5003; // 80% occupancy
1022 |
1023 | // GIF Image compression - modified 'compress'
1024 | //
1025 | // Based on: compress.c - File compression ala IEEE Computer, June 1984.
1026 | //
1027 | // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
1028 | // Jim McKie (decvax!mcvax!jim)
1029 | // Steve Davies (decvax!vax135!petsd!peora!srd)
1030 | // Ken Turkowski (decvax!decwrl!turtlevax!ken)
1031 | // James A. Woods (decvax!ihnp4!ames!jaw)
1032 | // Joe Orost (decvax!vax135!petsd!joe)
1033 |
1034 | int n_bits; // number of bits/code
1035 |
1036 | int maxbits = BITS; // user settable max # bits/code
1037 |
1038 | int maxcode; // maximum code, given n_bits
1039 |
1040 | int maxmaxcode = 1 << BITS; // should NEVER generate this code
1041 |
1042 | int[] htab = new int[HSIZE];
1043 |
1044 | int[] codetab = new int[HSIZE];
1045 |
1046 | int hsize = HSIZE; // for dynamic table sizing
1047 |
1048 | int free_ent = 0; // first unused entry
1049 |
1050 | // block compression parameters -- after all codes are used up,
1051 | // and compression rate changes, start over.
1052 | boolean clear_flg = false;
1053 |
1054 | // Algorithm: use open addressing double hashing (no chaining) on the
1055 | // prefix code / next character combination. We do a variant of Knuth's
1056 | // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
1057 | // secondary probe. Here, the modular division first probe is gives way
1058 | // to a faster exclusive-or manipulation. Also do block compression with
1059 | // an adaptive reset, whereby the code table is cleared when the compression
1060 | // ratio decreases, but after the table fills. The variable-length output
1061 | // codes are re-sized at this point, and a special CLEAR code is generated
1062 | // for the decompressor. Late addition: construct the table according to
1063 | // file size for noticeable speed improvement on small files. Please direct
1064 | // questions about this implementation to ames!jaw.
1065 |
1066 | int g_init_bits;
1067 |
1068 | int ClearCode;
1069 |
1070 | int EOFCode;
1071 |
1072 | // output
1073 | //
1074 | // Output the given code.
1075 | // Inputs:
1076 | // code: A n_bits-bit integer. If == -1, then EOF. This assumes
1077 | // that n_bits =< wordsize - 1.
1078 | // Outputs:
1079 | // Outputs code to the file.
1080 | // Assumptions:
1081 | // Chars are 8 bits long.
1082 | // Algorithm:
1083 | // Maintain a BITS character long buffer (so that 8 codes will
1084 | // fit in it exactly). Use the VAX insv instruction to insert each
1085 | // code in turn. When the buffer fills up empty it and start over.
1086 |
1087 | int cur_accum = 0;
1088 |
1089 | int cur_bits = 0;
1090 |
1091 | int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF,
1092 | 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
1093 |
1094 | // Number of characters so far in this 'packet'
1095 | int a_count;
1096 |
1097 | // Define the storage for the packet accumulator
1098 | byte[] accum = new byte[256];
1099 |
1100 | // ----------------------------------------------------------------------------
1101 | LZWEncoder(int width, int height, byte[] pixels, int color_depth) {
1102 | imgW = width;
1103 | imgH = height;
1104 | pixAry = pixels;
1105 | initCodeSize = Math.max(2, color_depth);
1106 | }
1107 |
1108 | // Add a character to the end of the current packet, and if it is 254
1109 | // characters, flush the packet to disk.
1110 | void char_out(byte c, OutputStream outs) throws IOException {
1111 | accum[a_count++] = c;
1112 | if (a_count >= 254)
1113 | flush_char(outs);
1114 | }
1115 |
1116 | // Clear out the hash table
1117 |
1118 | // table clear for block compress
1119 | void cl_block(OutputStream outs) throws IOException {
1120 | cl_hash(hsize);
1121 | free_ent = ClearCode + 2;
1122 | clear_flg = true;
1123 |
1124 | output(ClearCode, outs);
1125 | }
1126 |
1127 | // reset code table
1128 | void cl_hash(int hsize) {
1129 | for (int i = 0; i < hsize; ++i)
1130 | htab[i] = -1;
1131 | }
1132 |
1133 | void compress(int init_bits, OutputStream outs) throws IOException {
1134 | int fcode;
1135 | int i /* = 0 */;
1136 | int c;
1137 | int ent;
1138 | int disp;
1139 | int hsize_reg;
1140 | int hshift;
1141 |
1142 | // Set up the globals: g_init_bits - initial number of bits
1143 | g_init_bits = init_bits;
1144 |
1145 | // Set up the necessary values
1146 | clear_flg = false;
1147 | n_bits = g_init_bits;
1148 | maxcode = MAXCODE(n_bits);
1149 |
1150 | ClearCode = 1 << (init_bits - 1);
1151 | EOFCode = ClearCode + 1;
1152 | free_ent = ClearCode + 2;
1153 |
1154 | a_count = 0; // clear packet
1155 |
1156 | ent = nextPixel();
1157 |
1158 | hshift = 0;
1159 | for (fcode = hsize; fcode < 65536; fcode *= 2)
1160 | ++hshift;
1161 | hshift = 8 - hshift; // set hash code range bound
1162 |
1163 | hsize_reg = hsize;
1164 | cl_hash(hsize_reg); // clear hash table
1165 |
1166 | output(ClearCode, outs);
1167 |
1168 | outer_loop: while ((c = nextPixel()) != EOF) {
1169 | fcode = (c << maxbits) + ent;
1170 | i = (c << hshift) ^ ent; // xor hashing
1171 |
1172 | if (htab[i] == fcode) {
1173 | ent = codetab[i];
1174 | continue;
1175 | } else if (htab[i] >= 0) // non-empty slot
1176 | {
1177 | disp = hsize_reg - i; // secondary hash (after G. Knott)
1178 | if (i == 0)
1179 | disp = 1;
1180 | do {
1181 | if ((i -= disp) < 0)
1182 | i += hsize_reg;
1183 |
1184 | if (htab[i] == fcode) {
1185 | ent = codetab[i];
1186 | continue outer_loop;
1187 | }
1188 | } while (htab[i] >= 0);
1189 | }
1190 | output(ent, outs);
1191 | ent = c;
1192 | if (free_ent < maxmaxcode) {
1193 | codetab[i] = free_ent++; // code -> hashtable
1194 | htab[i] = fcode;
1195 | } else
1196 | cl_block(outs);
1197 | }
1198 | // Put out the final code.
1199 | output(ent, outs);
1200 | output(EOFCode, outs);
1201 | }
1202 |
1203 | // ----------------------------------------------------------------------------
1204 | void encode(OutputStream os) throws IOException {
1205 | os.write(initCodeSize); // write "initial code size" byte
1206 |
1207 | remaining = imgW * imgH; // reset navigation variables
1208 | curPixel = 0;
1209 |
1210 | compress(initCodeSize + 1, os); // compress and write the pixel data
1211 |
1212 | os.write(0); // write block terminator
1213 | }
1214 |
1215 | // Flush the packet to disk, and reset the accumulator
1216 | void flush_char(OutputStream outs) throws IOException {
1217 | if (a_count > 0) {
1218 | outs.write(a_count);
1219 | outs.write(accum, 0, a_count);
1220 | a_count = 0;
1221 | }
1222 | }
1223 |
1224 | final int MAXCODE(int n_bits) {
1225 | return (1 << n_bits) - 1;
1226 | }
1227 |
1228 | // ----------------------------------------------------------------------------
1229 | // Return the next pixel from the image
1230 | // ----------------------------------------------------------------------------
1231 | private int nextPixel() {
1232 | if (remaining == 0)
1233 | return EOF;
1234 |
1235 | --remaining;
1236 |
1237 | byte pix = pixAry[curPixel++];
1238 |
1239 | return pix & 0xff;
1240 | }
1241 |
1242 | void output(int code, OutputStream outs) throws IOException {
1243 | cur_accum &= masks[cur_bits];
1244 |
1245 | if (cur_bits > 0)
1246 | cur_accum |= (code << cur_bits);
1247 | else
1248 | cur_accum = code;
1249 |
1250 | cur_bits += n_bits;
1251 |
1252 | while (cur_bits >= 8) {
1253 | char_out((byte) (cur_accum & 0xff), outs);
1254 | cur_accum >>= 8;
1255 | cur_bits -= 8;
1256 | }
1257 |
1258 | // If the next entry is going to be too big for the code size,
1259 | // then increase it, if possible.
1260 | if (free_ent > maxcode || clear_flg) {
1261 | if (clear_flg) {
1262 | maxcode = MAXCODE(n_bits = g_init_bits);
1263 | clear_flg = false;
1264 | } else {
1265 | ++n_bits;
1266 | if (n_bits == maxbits)
1267 | maxcode = maxmaxcode;
1268 | else
1269 | maxcode = MAXCODE(n_bits);
1270 | }
1271 | }
1272 |
1273 | if (code == EOFCode) {
1274 | // At EOF, write the rest of the buffer.
1275 | while (cur_bits > 0) {
1276 | char_out((byte) (cur_accum & 0xff), outs);
1277 | cur_accum >>= 8;
1278 | cur_bits -= 8;
1279 | }
1280 |
1281 | flush_char(outs);
1282 | }
1283 | }
1284 | }
--------------------------------------------------------------------------------