1、项目简介

  • 项目名称:pic2gif
  • 项目实现:将选定的几张图片(支持jpg,png,bmp,gif等) 合成为一张gif图
  • 关键字:图片处理,Java,Swing

2、项目结构

  • 图片处理部分
  • Swing界面部分 (u1s1, Swing真的不行)

3、代码展示

3.1 Util

3.1.1 AnimatedGifEncoder.java
/**   * @Package  com.leeannm.util* @Function  AnimatedGifEncoder.java * @version  1.0.0* @Description 1.开源代码,这里直接复制到util包使用*/
package com.leeannm.util;import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;public class AnimatedGifEncoder {protected int width; // image sizeprotected int height;protected Color transparent = null; // transparent color if givenprotected int transIndex; // transparent index in color tableprotected int repeat = -1; // no repeatprotected int delay = 0; // frame delay (hundredths)protected boolean started = false; // ready to output framesprotected OutputStream out;protected BufferedImage image; // current frameprotected byte[] pixels; // BGR byte array from frameprotected byte[] indexedPixels; // converted frame indexed to paletteprotected int colorDepth; // number of bit planesprotected byte[] colorTab; // RGB paletteprotected boolean[] usedEntry = new boolean[256]; // active palette entriesprotected int palSize = 7; // color table size (bits-1)protected int dispose = -1; // disposal code (-1 = use default)protected boolean closeStream = false; // close stream when finishedprotected boolean firstFrame = true;protected boolean sizeSet = false; // if false, get size from first frameprotected int sample = 10; // default sample interval for quantizer/*** Sets the delay time between each frame, or changes it for subsequent frames* (applies to last frame added).* * @param ms*          int delay time in milliseconds*/public void setDelay(int ms) {delay = Math.round(ms / 10.0f);}/*** Sets the GIF frame disposal code for the last added frame and any* subsequent frames. Default is 0 if no transparent color has been set,* otherwise 2.* * @param code*          int disposal code.*/public void setDispose(int code) {if (code >= 0) {dispose = code;}}/*** Sets the number of times the set of GIF frames should be played. Default is* 1; 0 means play indefinitely. Must be invoked before the first image is* added.* * @param iter*          int number of iterations.* @return*/public void setRepeat(int iter) {if (iter >= 0) {repeat = iter;}}/*** Sets the transparent color for the last added frame and any subsequent* frames. Since all colors are subject to modification in the quantization* process, the color in the final palette for each frame closest to the given* color becomes the transparent color for that frame. May be set to null to* indicate no transparent color.* * @param c*          Color to be treated as transparent on display.*/public void setTransparent(Color c) {transparent = c;}/*** Adds next GIF frame. The frame is not written immediately, but is actually* deferred until the next frame is received so that timing data can be* inserted. Invoking <code>finish()</code> flushes all frames. If* <code>setSize</code> was not invoked, the size of the first image is used* for all subsequent frames.* * @param im*          BufferedImage containing frame to write.* @return true if successful.*/public boolean addFrame(BufferedImage im) {if ((im == null) || !started) {return false;}boolean ok = true;try {if (!sizeSet) {// use first frame's sizesetSize(im.getWidth(), im.getHeight());}image = im;getImagePixels(); // convert to correct format if necessaryanalyzePixels(); // build color table & map pixelsif (firstFrame) {writeLSD(); // logical screen descriptiorwritePalette(); // global color tableif (repeat >= 0) {// use NS app extension to indicate repswriteNetscapeExt();}}writeGraphicCtrlExt(); // write graphic control extensionwriteImageDesc(); // image descriptorif (!firstFrame) {writePalette(); // local color table}writePixels(); // encode and write pixel datafirstFrame = false;} catch (IOException e) {ok = false;}return ok;}/*** Flushes any pending data and closes output file. If writing to an* OutputStream, the stream is not closed.*/public boolean finish() {if (!started)return false;boolean ok = true;started = false;try {out.write(0x3b); // gif trailerout.flush();if (closeStream) {out.close();}} catch (IOException e) {ok = false;}// reset for subsequent usetransIndex = 0;out = null;image = null;pixels = null;indexedPixels = null;colorTab = null;closeStream = false;firstFrame = true;return ok;}/*** Sets frame rate in frames per second. Equivalent to* <code>setDelay(1000/fps)</code>.* * @param fps*          float frame rate (frames per second)*/public void setFrameRate(float fps) {if (fps != 0f) {delay = Math.round(100f / fps);}}/*** Sets quality of color quantization (conversion of images to the maximum 256* colors allowed by the GIF specification). Lower values (minimum = 1)* produce better colors, but slow processing significantly. 10 is the* default, and produces good color mapping at reasonable speeds. Values* greater than 20 do not yield significant improvements in speed.* * @param quality*          int greater than 0.* @return*/public void setQuality(int quality) {if (quality < 1)quality = 1;sample = quality;}/*** Sets the GIF frame size. The default size is the size of the first frame* added if this method is not invoked.* * @param w*          int frame width.* @param h*          int frame width.*/public void setSize(int w, int h) {if (started && !firstFrame)return;width = w;height = h;if (width < 1)width = 320;if (height < 1)height = 240;sizeSet = true;}/*** Initiates GIF file creation on the given stream. The stream is not closed* automatically.* * @param os*          OutputStream on which GIF images are written.* @return false if initial write failed.*/public boolean start(OutputStream os) {if (os == null)return false;boolean ok = true;closeStream = false;out = os;try {writeString("GIF89a"); // header} catch (IOException e) {ok = false;}return started = ok;}/*** Initiates writing of a GIF file with the specified name.* * @param file*          String containing output file name.* @return false if open or initial write failed.*/public boolean start(String file) {boolean ok = true;try {out = new BufferedOutputStream(new FileOutputStream(file));ok = start(out);closeStream = true;} catch (IOException e) {ok = false;}return started = ok;}/*** Analyzes image colors and creates color map.*/protected void analyzePixels() {int len = pixels.length;int nPix = len / 3;indexedPixels = new byte[nPix];NeuQuant nq = new NeuQuant(pixels, len, sample);// initialize quantizercolorTab = nq.process(); // create reduced palette// convert map from BGR to RGBfor (int i = 0; i < colorTab.length; i += 3) {byte temp = colorTab[i];colorTab[i] = colorTab[i + 2];colorTab[i + 2] = temp;usedEntry[i / 3] = false;}// map image pixels to new paletteint k = 0;for (int i = 0; i < nPix; i++) {int index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff);usedEntry[index] = true;indexedPixels[i] = (byte) index;}pixels = null;colorDepth = 8;palSize = 7;// get closest match to transparent color if specifiedif (transparent != null) {transIndex = findClosest(transparent);}}/*** Returns index of palette color closest to c* */protected int findClosest(Color c) {if (colorTab == null)return -1;int r = c.getRed();int g = c.getGreen();int b = c.getBlue();int minpos = 0;int dmin = 256 * 256 * 256;int len = colorTab.length;for (int i = 0; i < len;) {int dr = r - (colorTab[i++] & 0xff);int dg = g - (colorTab[i++] & 0xff);int db = b - (colorTab[i] & 0xff);int d = dr * dr + dg * dg + db * db;int index = i / 3;if (usedEntry[index] && (d < dmin)) {dmin = d;minpos = index;}i++;}return minpos;}/*** Extracts image pixels into byte array "pixels"*/protected void getImagePixels() {int w = image.getWidth();int h = image.getHeight();int type = image.getType();if ((w != width) || (h != height) || (type != BufferedImage.TYPE_3BYTE_BGR)) {// create new image with right size/formatBufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);Graphics2D g = temp.createGraphics();g.drawImage(image, 0, 0, null);image = temp;}pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();}/*** Writes Graphic Control Extension*/protected void writeGraphicCtrlExt() throws IOException {out.write(0x21); // extension introducerout.write(0xf9); // GCE labelout.write(4); // data block sizeint transp, disp;if (transparent == null) {transp = 0;disp = 0; // dispose = no action} else {transp = 1;disp = 2; // force clear if using transparent color}if (dispose >= 0) {disp = dispose & 7; // user override}disp <<= 2;// packed fieldsout.write(0 | // 1:3 reserveddisp | // 4:6 disposal0 | // 7 user input - 0 = nonetransp); // 8 transparency flagwriteShort(delay); // delay x 1/100 secout.write(transIndex); // transparent color indexout.write(0); // block terminator}/*** Writes Image Descriptor*/protected void writeImageDesc() throws IOException {out.write(0x2c); // image separatorwriteShort(0); // image position x,y = 0,0writeShort(0);writeShort(width); // image sizewriteShort(height);// packed fieldsif (firstFrame) {// no LCT - GCT is used for first (or only) frameout.write(0);} else {// specify normal LCTout.write(0x80 | // 1 local color table 1=yes0 | // 2 interlace - 0=no0 | // 3 sorted - 0=no0 | // 4-5 reservedpalSize); // 6-8 size of color table}}/*** Writes Logical Screen Descriptor*/protected void writeLSD() throws IOException {// logical screen sizewriteShort(width);writeShort(height);// packed fieldsout.write((0x80 | // 1 : global color table flag = 1 (gct used)0x70 | // 2-4 : color resolution = 70x00 | // 5 : gct sort flag = 0palSize)); // 6-8 : gct sizeout.write(0); // background color indexout.write(0); // pixel aspect ratio - assume 1:1}/*** Writes Netscape application extension to define repeat count.*/protected void writeNetscapeExt() throws IOException {out.write(0x21); // extension introducerout.write(0xff); // app extension labelout.write(11); // block sizewriteString("NETSCAPE" + "2.0"); // app id + auth codeout.write(3); // sub-block sizeout.write(1); // loop sub-block idwriteShort(repeat); // loop count (extra iterations, 0=repeat forever)out.write(0); // block terminator}/*** Writes color table*/protected void writePalette() throws IOException {out.write(colorTab, 0, colorTab.length);int n = (3 * 256) - colorTab.length;for (int i = 0; i < n; i++) {out.write(0);}}/*** Encodes and writes pixel data*/protected void writePixels() throws IOException {LZWEncoder encoder = new LZWEncoder(width, height, indexedPixels, colorDepth);encoder.encode(out);}/*** Write 16-bit value to output stream, LSB first*/protected void writeShort(int value) throws IOException {out.write(value & 0xff);out.write((value >> 8) & 0xff);}/*** Writes string to output stream*/protected void writeString(String s) throws IOException {for (int i = 0; i < s.length(); i++) {out.write((byte) s.charAt(i));}}
}//
// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
// K Weiner 12/00class LZWEncoder {private static final int EOF = -1;private int imgW, imgH;private byte[] pixAry;private int initCodeSize;private int remaining;private int curPixel;// GIFCOMPR.C - GIF Image compression routines//// Lempel-Ziv compression based on 'compress'. GIF modifications by// David Rowley (mgardi@watdcsu.waterloo.edu)// General DEFINEsstatic final int BITS = 12;static final int HSIZE = 5003; // 80% occupancy// GIF Image compression - modified 'compress'//// Based on: compress.c - File compression ala IEEE Computer, June 1984.//// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)// Jim McKie (decvax!mcvax!jim)// Steve Davies (decvax!vax135!petsd!peora!srd)// Ken Turkowski (decvax!decwrl!turtlevax!ken)// James A. Woods (decvax!ihnp4!ames!jaw)// Joe Orost (decvax!vax135!petsd!joe)int n_bits; // number of bits/codeint maxbits = BITS; // user settable max # bits/codeint maxcode; // maximum code, given n_bitsint maxmaxcode = 1 << BITS; // should NEVER generate this codeint[] htab = new int[HSIZE];int[] codetab = new int[HSIZE];int hsize = HSIZE; // for dynamic table sizingint free_ent = 0; // first unused entry// block compression parameters -- after all codes are used up,// and compression rate changes, start over.boolean clear_flg = false;// Algorithm: use open addressing double hashing (no chaining) on the// prefix code / next character combination. We do a variant of Knuth's// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime// secondary probe. Here, the modular division first probe is gives way// to a faster exclusive-or manipulation. Also do block compression with// an adaptive reset, whereby the code table is cleared when the compression// ratio decreases, but after the table fills. The variable-length output// codes are re-sized at this point, and a special CLEAR code is generated// for the decompressor. Late addition: construct the table according to// file size for noticeable speed improvement on small files. Please direct// questions about this implementation to ames!jaw.int g_init_bits;int ClearCode;int EOFCode;// output//// Output the given code.// Inputs:// code: A n_bits-bit integer. If == -1, then EOF. This assumes// that n_bits =< wordsize - 1.// Outputs:// Outputs code to the file.// Assumptions:// Chars are 8 bits long.// Algorithm:// Maintain a BITS character long buffer (so that 8 codes will// fit in it exactly). Use the VAX insv instruction to insert each// code in turn. When the buffer fills up empty it and start over.int cur_accum = 0;int cur_bits = 0;int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF,0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };// Number of characters so far in this 'packet'int a_count;// Define the storage for the packet accumulatorbyte[] accum = new byte[256];// ----------------------------------------------------------------------------LZWEncoder(int width, int height, byte[] pixels, int color_depth) {imgW = width;imgH = height;pixAry = pixels;initCodeSize = Math.max(2, color_depth);}// Add a character to the end of the current packet, and if it is 254// characters, flush the packet to disk.void char_out(byte c, OutputStream outs) throws IOException {accum[a_count++] = c;if (a_count >= 254)flush_char(outs);}// Clear out the hash table// table clear for block compressvoid cl_block(OutputStream outs) throws IOException {cl_hash(hsize);free_ent = ClearCode + 2;clear_flg = true;output(ClearCode, outs);}// reset code tablevoid cl_hash(int hsize) {for (int i = 0; i < hsize; ++i)htab[i] = -1;}void compress(int init_bits, OutputStream outs) throws IOException {int fcode;int i /* = 0 */;int c;int ent;int disp;int hsize_reg;int hshift;// Set up the globals: g_init_bits - initial number of bitsg_init_bits = init_bits;// Set up the necessary valuesclear_flg = false;n_bits = g_init_bits;maxcode = MAXCODE(n_bits);ClearCode = 1 << (init_bits - 1);EOFCode = ClearCode + 1;free_ent = ClearCode + 2;a_count = 0; // clear packetent = nextPixel();hshift = 0;for (fcode = hsize; fcode < 65536; fcode *= 2)++hshift;hshift = 8 - hshift; // set hash code range boundhsize_reg = hsize;cl_hash(hsize_reg); // clear hash tableoutput(ClearCode, outs);outer_loop: while ((c = nextPixel()) != EOF) {fcode = (c << maxbits) + ent;i = (c << hshift) ^ ent; // xor hashingif (htab[i] == fcode) {ent = codetab[i];continue;} else if (htab[i] >= 0) // non-empty slot{disp = hsize_reg - i; // secondary hash (after G. Knott)if (i == 0)disp = 1;do {if ((i -= disp) < 0)i += hsize_reg;if (htab[i] == fcode) {ent = codetab[i];continue outer_loop;}} while (htab[i] >= 0);}output(ent, outs);ent = c;if (free_ent < maxmaxcode) {codetab[i] = free_ent++; // code -> hashtablehtab[i] = fcode;} elsecl_block(outs);}// Put out the final code.output(ent, outs);output(EOFCode, outs);}// ----------------------------------------------------------------------------void encode(OutputStream os) throws IOException {os.write(initCodeSize); // write "initial code size" byteremaining = imgW * imgH; // reset navigation variablescurPixel = 0;compress(initCodeSize + 1, os); // compress and write the pixel dataos.write(0); // write block terminator}// Flush the packet to disk, and reset the accumulatorvoid flush_char(OutputStream outs) throws IOException {if (a_count > 0) {outs.write(a_count);outs.write(accum, 0, a_count);a_count = 0;}}final int MAXCODE(int n_bits) {return (1 << n_bits) - 1;}// ----------------------------------------------------------------------------// Return the next pixel from the image// ----------------------------------------------------------------------------private int nextPixel() {if (remaining == 0)return EOF;--remaining;byte pix = pixAry[curPixel++];return pix & 0xff;}void output(int code, OutputStream outs) throws IOException {cur_accum &= masks[cur_bits];if (cur_bits > 0)cur_accum |= (code << cur_bits);elsecur_accum = code;cur_bits += n_bits;while (cur_bits >= 8) {char_out((byte) (cur_accum & 0xff), outs);cur_accum >>= 8;cur_bits -= 8;}// If the next entry is going to be too big for the code size,// then increase it, if possible.if (free_ent > maxcode || clear_flg) {if (clear_flg) {maxcode = MAXCODE(n_bits = g_init_bits);clear_flg = false;} else {++n_bits;if (n_bits == maxbits)maxcode = maxmaxcode;elsemaxcode = MAXCODE(n_bits);}}if (code == EOFCode) {// At EOF, write the rest of the buffer.while (cur_bits > 0) {char_out((byte) (cur_accum & 0xff), outs);cur_accum >>= 8;cur_bits -= 8;}flush_char(outs);}}
}/** NeuQuant Neural-Net Quantization Algorithm* ------------------------------------------* * Copyright (c) 1994 Anthony Dekker* * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See* "Kohonen neural networks for optimal colour quantization" in "Network:* Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of* the algorithm.* * Any party obtaining a copy of these files from the author, directly or* indirectly, is granted, free of charge, a full and unrestricted irrevocable,* world-wide, paid up, royalty-free, nonexclusive right and license to deal in* this software and documentation files (the "Software"), including without* limitation the rights to use, copy, modify, merge, publish, distribute,* sublicense, and/or sell copies of the Software, and to permit persons who* receive copies from any such party to do so, with the only requirement being* that this copyright notice remain intact.*/// Ported to Java 12/00 K Weiner
class NeuQuant {protected static final int netsize = 256; /* number of colours used *//* four primes near 500 - assume no image has a length so large *//* that it is divisible by all four primes */protected static final int prime1 = 499;protected static final int prime2 = 491;protected static final int prime3 = 487;protected static final int prime4 = 503;protected static final int minpicturebytes = (3 * prime4);/* minimum size for input image *//** Program Skeleton ---------------- [select samplefac in range 1..30] [read* image from input file] pic = (unsigned char*) malloc(3*width*height);* initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output* image header, using writecolourmap(f)] inxbuild(); write output image using* inxsearch(b,g,r)*//** Network Definitions -------------------*/protected static final int maxnetpos = (netsize - 1);protected static final int netbiasshift = 4; /* bias for colour values */protected static final int ncycles = 100; /* no. of learning cycles *//* defs for freq and bias */protected static final int intbiasshift = 16; /* bias for fractions */protected static final int intbias = (((int) 1) << intbiasshift);protected static final int gammashift = 10; /* gamma = 1024 */protected static final int gamma = (((int) 1) << gammashift);protected static final int betashift = 10;protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */protected static final int betagamma = (intbias << (gammashift - betashift));/* defs for decreasing radius factor */protected static final int initrad = (netsize >> 3); /** for 256 cols, radius* starts*/protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */protected static final int radiusbias = (((int) 1) << radiusbiasshift);protected static final int initradius = (initrad * radiusbias); /** and* decreases* by a*/protected static final int radiusdec = 30; /* factor of 1/30 each cycle *//* defs for decreasing alpha factor */protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */protected static final int initalpha = (((int) 1) << alphabiasshift);protected int alphadec; /* biased by 10 bits *//* radbias and alpharadbias used for radpower calculation */protected static final int radbiasshift = 8;protected static final int radbias = (((int) 1) << radbiasshift);protected static final int alpharadbshift = (alphabiasshift + radbiasshift);protected static final int alpharadbias = (((int) 1) << alpharadbshift);/** Types and Global Variables --------------------------*/protected byte[] thepicture; /* the input image itself */protected int lengthcount; /* lengthcount = H*W*3 */protected int samplefac; /* sampling factor 1..30 */// typedef int pixel[4]; /* BGRc */protected int[][] network; /* the network itself - [netsize][4] */protected int[] netindex = new int[256];/* for network lookup - really 256 */protected int[] bias = new int[netsize];/* bias and freq arrays for learning */protected int[] freq = new int[netsize];protected int[] radpower = new int[initrad];/* radpower for precomputation *//** Initialise network in range (0,0,0) to (255,255,255) and set parameters* -----------------------------------------------------------------------*/public NeuQuant(byte[] thepic, int len, int sample) {int i;int[] p;thepicture = thepic;lengthcount = len;samplefac = sample;network = new int[netsize][];for (i = 0; i < netsize; i++) {network[i] = new int[4];p = network[i];p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;freq[i] = intbias / netsize; /* 1/netsize */bias[i] = 0;}}public byte[] colorMap() {byte[] map = new byte[3 * netsize];int[] index = new int[netsize];for (int i = 0; i < netsize; i++)index[network[i][3]] = i;int k = 0;for (int i = 0; i < netsize; i++) {int j = index[i];map[k++] = (byte) (network[j][0]);map[k++] = (byte) (network[j][1]);map[k++] = (byte) (network[j][2]);}return map;}/** Insertion sort of network and building of netindex[0..255] (to do after* unbias)* -------------------------------------------------------------------------------*/public void inxbuild() {int i, j, smallpos, smallval;int[] p;int[] q;int previouscol, startpos;previouscol = 0;startpos = 0;for (i = 0; i < netsize; i++) {p = network[i];smallpos = i;smallval = p[1]; /* index on g *//* find smallest in i..netsize-1 */for (j = i + 1; j < netsize; j++) {q = network[j];if (q[1] < smallval) { /* index on g */smallpos = j;smallval = q[1]; /* index on g */}}q = network[smallpos];/* swap p (i) and q (smallpos) entries */if (i != smallpos) {j = q[0];q[0] = p[0];p[0] = j;j = q[1];q[1] = p[1];p[1] = j;j = q[2];q[2] = p[2];p[2] = j;j = q[3];q[3] = p[3];p[3] = j;}/* smallval entry is now in position i */if (smallval != previouscol) {netindex[previouscol] = (startpos + i) >> 1;for (j = previouscol + 1; j < smallval; j++)netindex[j] = i;previouscol = smallval;startpos = i;}}netindex[previouscol] = (startpos + maxnetpos) >> 1;for (j = previouscol + 1; j < 256; j++)netindex[j] = maxnetpos; /* really 256 */}/** Main Learning Loop ------------------*/public void learn() {int i, j, b, g, r;int radius, rad, alpha, step, delta, samplepixels;byte[] p;int pix, lim;if (lengthcount < minpicturebytes)samplefac = 1;alphadec = 30 + ((samplefac - 1) / 3);p = thepicture;pix = 0;lim = lengthcount;samplepixels = lengthcount / (3 * samplefac);delta = samplepixels / ncycles;alpha = initalpha;radius = initradius;rad = radius >> radiusbiasshift;if (rad <= 1)rad = 0;for (i = 0; i < rad; i++)radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));// fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad);if (lengthcount < minpicturebytes)step = 3;else if ((lengthcount % prime1) != 0)step = 3 * prime1;else {if ((lengthcount % prime2) != 0)step = 3 * prime2;else {if ((lengthcount % prime3) != 0)step = 3 * prime3;elsestep = 3 * prime4;}}i = 0;while (i < samplepixels) {b = (p[pix + 0] & 0xff) << netbiasshift;g = (p[pix + 1] & 0xff) << netbiasshift;r = (p[pix + 2] & 0xff) << netbiasshift;j = contest(b, g, r);altersingle(alpha, j, b, g, r);if (rad != 0)alterneigh(rad, j, b, g, r); /* alter neighbours */pix += step;if (pix >= lim)pix -= lengthcount;i++;if (delta == 0)delta = 1;if (i % delta == 0) {alpha -= alpha / alphadec;radius -= radius / radiusdec;rad = radius >> radiusbiasshift;if (rad <= 1)rad = 0;for (j = 0; j < rad; j++)radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));}}// fprintf(stderr,"finished 1D learning: final alpha=%f// !\n",((float)alpha)/initalpha);}/** Search for BGR values 0..255 (after net is unbiased) and return colour* index* ----------------------------------------------------------------------------*/public int map(int b, int g, int r) {int i, j, dist, a, bestd;int[] p;int best;bestd = 1000; /* biggest possible dist is 256*3 */best = -1;i = netindex[g]; /* index on g */j = i - 1; /* start at netindex[g] and work outwards */while ((i < netsize) || (j >= 0)) {if (i < netsize) {p = network[i];dist = p[1] - g; /* inx key */if (dist >= bestd)i = netsize; /* stop iter */else {i++;if (dist < 0)dist = -dist;a = p[0] - b;if (a < 0)a = -a;dist += a;if (dist < bestd) {a = p[2] - r;if (a < 0)a = -a;dist += a;if (dist < bestd) {bestd = dist;best = p[3];}}}}if (j >= 0) {p = network[j];dist = g - p[1]; /* inx key - reverse dif */if (dist >= bestd)j = -1; /* stop iter */else {j--;if (dist < 0)dist = -dist;a = p[0] - b;if (a < 0)a = -a;dist += a;if (dist < bestd) {a = p[2] - r;if (a < 0)a = -a;dist += a;if (dist < bestd) {bestd = dist;best = p[3];}}}}}return (best);}public byte[] process() {learn();unbiasnet();inxbuild();return colorMap();}/** Unbias network to give byte values 0..255 and record position i to prepare* for sort* -----------------------------------------------------------------------------------*/public void unbiasnet() {for (int i = 0; i < netsize; i++) {network[i][0] >>= netbiasshift;network[i][1] >>= netbiasshift;network[i][2] >>= netbiasshift;network[i][3] = i; /* record colour no */}}/** Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in* radpower[|i-j|]* ---------------------------------------------------------------------------------*/protected void alterneigh(int rad, int i, int b, int g, int r) {int j, k, lo, hi, a, m;int[] p;lo = i - rad;if (lo < -1)lo = -1;hi = i + rad;if (hi > netsize)hi = netsize;j = i + 1;k = i - 1;m = 1;while ((j < hi) || (k > lo)) {a = radpower[m++];if (j < hi) {p = network[j++];try {p[0] -= (a * (p[0] - b)) / alpharadbias;p[1] -= (a * (p[1] - g)) / alpharadbias;p[2] -= (a * (p[2] - r)) / alpharadbias;} catch (Exception e) {} // prevents 1.3 miscompilation}if (k > lo) {p = network[k--];try {p[0] -= (a * (p[0] - b)) / alpharadbias;p[1] -= (a * (p[1] - g)) / alpharadbias;p[2] -= (a * (p[2] - r)) / alpharadbias;} catch (Exception e) {}}}}/** Move neuron i towards biased (b,g,r) by factor alpha* ----------------------------------------------------*/protected void altersingle(int alpha, int i, int b, int g, int r) {/* alter hit neuron */int[] n = network[i];n[0] -= (alpha * (n[0] - b)) / initalpha;n[1] -= (alpha * (n[1] - g)) / initalpha;n[2] -= (alpha * (n[2] - r)) / initalpha;}/** Search for biased BGR values ----------------------------*/protected int contest(int b, int g, int r) {/* finds closest neuron (min dist) and updates freq *//* finds best neuron (min dist-bias) and returns position *//* for frequently chosen neurons, freq[i] is high and bias[i] is negative *//* bias[i] = gamma*((1/netsize)-freq[i]) */int i, dist, a, biasdist, betafreq;int bestpos, bestbiaspos, bestd, bestbiasd;int[] n;bestd = ~(((int) 1) << 31);bestbiasd = bestd;bestpos = -1;bestbiaspos = bestpos;for (i = 0; i < netsize; i++) {n = network[i];dist = n[0] - b;if (dist < 0)dist = -dist;a = n[1] - g;if (a < 0)a = -a;dist += a;a = n[2] - r;if (a < 0)a = -a;dist += a;if (dist < bestd) {bestd = dist;bestpos = i;}biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));if (biasdist < bestbiasd) {bestbiasd = biasdist;bestbiaspos = i;}betafreq = (freq[i] >> betashift);freq[i] -= betafreq;bias[i] += (betafreq << gammashift);}freq[bestpos] += beta;bias[bestpos] -= betagamma;return (bestbiaspos);}
}
3.1.2 PicToGif.java
/** * Copyright © 2021 All rights reserved* * @Package com.leeannm.util* @Function PicToGif.java* @author  LIAN   * @date    2021年2月4日 下午6:36:05 * @Description 2.调用方法,实现图片合成的代码逻辑,有参考*/
package com.leeannm.util;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;public class PicToGif {public static void main(String[] args) {//非程序入口,测试用File file=new File("F://1");//以下5行是在检测资源所在的文件夹if(!file.exists()){//如果文件夹不存在file.mkdir();//创建文件夹}String str1 = "F://1//1.jpg"; //以下5行是在配置输入的imgList,类似的实现即可String str2 = "F://1//2.png";String[] pic = new String[2];pic[0] = str2;pic[1] = str1;String newPic = "F://1//output.gif"; //输出路径int playTime = 500;              //时间间隔jpgToGif(pic,newPic,playTime);    //执行静态函数(imgList,输出路径,时间间隔)System.out.println("结束!");}/*** @param pic String[] 多个jpg文件名 包含路径* @param newPic String 生成的gif文件名 包含路径* @param playTime int 播放的延迟时间* @Description 把多张图片合成一张(将新图片插入到gif图末尾中)*/public synchronized static void jpgToGif(String[] pic, String newPic, int playTime) {try {AnimatedGifEncoder e = new AnimatedGifEncoder();e.setRepeat(0);e.start(newPic);BufferedImage[] src = new BufferedImage[pic.length];for (int i = 0; i < src.length; i++) {e.setDelay(playTime); //设置播放的延迟时间src[i] = ImageIO.read(new File(pic[i])); // 读入需要播放的jpg文件e.addFrame(src[i]);  //添加到帧中}e.finish();} catch (Exception e) {System.out.println( "jpgToGif Failed:");e.printStackTrace();}}/*** @Description 将上一函数,扩展为可自定义图片数量*/public synchronized static void jpgToGif(String[] pic, int imgNum, String newPic, int playTime) {try {AnimatedGifEncoder e = new AnimatedGifEncoder();e.setRepeat(0);e.start(newPic+"\\output.gif");BufferedImage[] src = new BufferedImage[imgNum];for (int i = 0; i < imgNum; i++) {e.setDelay(playTime); //设置播放的延迟时间src[i] = ImageIO.read(new File(pic[i])); // 读入需要播放的jpg文件e.addFrame(src[i]);  //添加到帧中}e.finish();} catch (Exception e) {System.out.println( "jpgToGif Failed:");e.printStackTrace();}}}
3.1.3 ImageLabel.java
/**   * Copyright © 2021 All rights reserved* * @Package com.leeannm.util* @Function ImageLabel.java* @author  LIAN   * @date    2021年2月4日 下午6:42:40 * @Description 将指定路径的图片缩放到指定大小,生成ImageLabel对象*/
package img;import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;public class ImageLabel extends JLabel {public ImageLabel(String iconPath) {ImageIcon icon = new ImageIcon(zoomImage(iconPath, 400,250));//icon.getImage().flush();setIcon(icon);setIconTextGap(0);setBorder(null);setText(null);setOpaque(false);}public ImageLabel(String iconPath, int width,int height) {ImageIcon icon = new ImageIcon(zoomImage(iconPath, width, height));setIcon(icon);setIconTextGap(0);setBorder(null);setText(null);setOpaque(false);}/**** @Function ImageLabel.java* @Description 将指定的图片缩放到指定大小** @return BufferedImage* @version v1.0.0* @author LIAN* @date 2021年2月10日 上午10:44:09*/public BufferedImage zoomImage(String path, int width, int height) {BufferedImage image = null;try {image = ImageIO.read(new File(path));} catch (IOException e) {System.out.println("读图失败");e.printStackTrace();}assert image != null;double scaleWidth = (double) width / (double) image.getWidth();double scaleHeight = (double) height / (double) image.getHeight();BufferedImage dstImage = new BufferedImage(width, height, image.getType());AffineTransform affineTransform = new AffineTransform();affineTransform.scale(scaleWidth , scaleHeight );AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform,null);affineTransformOp.filter(image, dstImage);return dstImage;}
}

3.2 UI

3.2.1 界面参考
  • GUIDesignStudio设计图:

  • 过程中产生的图:


  • 最终结果图:

3.2.2 UI7.java

​ (比较懒所以全部写在一个文件里了,但是代码层次注释的还是很清晰的。其中运用到了多种不同的布局方式,算是对Swing的回顾温习)

/**   * Copyright © 2021 All rights reserved* * @Package com.leeannm.ui* @Function UI7.java* @version 1.7.0* @author  LIAN   * @date    2021年2月4日 下午4:31:29 * @Description */
package com.leeannm.ui;import com.leeannm.util.ImageLabel;
import com.leeannm.util.PicToGif;import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.Hashtable;
import java.util.List;public class UI7 extends JFrame{//窗体总体由左右两部分组成。private JPanel rightPanel; //右部分由上下两部分构成,具体实现在静态函数里搭建private JPanel leftPanel;  //左部分也是上下结构,搭建同上private JPanel imgPanel; //左侧,上方,图片展示区/拖拽区private final JTextField tf2 =new JTextField("未选择路径",20);//输出路径private boolean hasPath = false;//是否选定了输出路径private ImageLabel img;private final JLabel msg =new JLabel(" ",JLabel.RIGHT);//所有操作的响应信息private static final int maxNum = 30; //最大选择数量(可以更改哦)private int interval = 200;   //时间间隔private Timer timer;private int imgNum = 0;      //已选图片数量private int imgIndex = 0;  //指示展示的图片的序号private final String[] imgNameList = new String[maxNum];//只保存文件名,便于阅读private final String[] imgPathList = new String[maxNum];//保存完整文件路径+文件名public static void main(String[] args) {UI7 frame= new UI7();frame.pack();    //自动调整大小frame.setVisible(true);//设置窗口可见frame.toFront();//设置窗口最前}public UI7() {setTitle("图片合成GIF--LI");//设置窗口标题/*窗体创建*/setResizable(true); //设置是否可以调整窗口大小setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setVisible(true);    //设置窗口初始是否可见//setIconImage(); //设置窗口图标setBounds(500, 200, 600, 400);//设置窗口位置、大小//setBounds(500, 200, 250, 400);/*窗体内容*/createRightPanel();createLeftPanel();JPanel panelContainer = new JPanel(new BorderLayout());JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,false, leftPanel,rightPanel);splitPane.setResizeWeight(0.7);//设置分隔权重splitPane.setOneTouchExpandable(false);//设置JSplitPane是否可以展开或收起splitPane.setDividerSize(5);// 设置分隔线宽度的大小,以pixel为计算单位。panelContainer.add(splitPane,BorderLayout.CENTER);setContentPane(panelContainer);}private void createRightPanel() {rightPanel =new JPanel();BoxLayout layout = new BoxLayout(rightPanel,BoxLayout.Y_AXIS);rightPanel.setLayout(layout);JLabel panelLabel =new JLabel("输出选项  ",JLabel.LEFT);//所需组件:2label标签,1文本框,2按钮,1下拉, 1滑块JLabel label1 = new JLabel("时间间隔(ms):");//标签JLabel label2 = new JLabel("尺寸选择:");JLabel label3 = new JLabel("输出路径:");JLabel label4 = new JLabel(String.valueOf(interval));label4.setOpaque(true);label4.setBackground(Color.WHITE);tf2.setHorizontalAlignment(JTextField.LEFT);JButton button1 = new JButton("选择路径");//按钮//匿名内部类button1.addActionListener(arg0 -> {JFileChooser fc=new JFileChooser("F:\\");fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);//设置只能选择目录int val=fc.showOpenDialog(null);//文件打开对话框if(val==JFileChooser.APPROVE_OPTION){//正常选择路径tf2.setText(fc.getSelectedFile().toString());hasPath = true;msg.setText("已选择路径!");}else{//未正常选择路径,如选择取消按钮hasPath = false;msg.setText("未选择路径!");}});JButton button2 = new JButton("生成GIF");button2.addActionListener(e -> {String text = checkConditions();if(text==null) {//满足条件,执行函数PicToGif.jpgToGif(getImgList(),getImgNum(),tf2.getText(),interval);}else {//不满足则输出信息msg.setText(text);}});JComboBox<String> cmb = new JComboBox<>();//下拉框cmb.insertItemAt("360P", 0);//下拉框的选项cmb.insertItemAt("720P", 1);cmb.insertItemAt("1080P", 2);JSlider slider = new JSlider(30,500,getInterval());//滑块Hashtable<Integer, JComponent> hashtable = new Hashtable<>();hashtable.put(30, new JLabel("30"));//滑块上的自定义标签hashtable.put(90, new JLabel("90"));hashtable.put(200, new JLabel("200"));hashtable.put(500, new JLabel("500"));slider.setLabelTable(hashtable);//将标签设置到滑块slider.setPaintLabels(true);//绘制标签slider.addChangeListener(e -> {setInterval(slider.getValue());timer.setDelay(interval);label4.setText(String.valueOf(slider.getValue()));});//依次添加panelJPanel p0 = new JPanel();p0.setLayout(new BoxLayout(p0,BoxLayout.X_AXIS));p0.setBackground(Color.GRAY);p0.add(panelLabel);p0.add(Box.createGlue());JPanel p1 =new JPanel();p1.add(label1);p1.add(label4);JPanel p2 = new JPanel();p2.setLayout(new BoxLayout(p2,BoxLayout.Y_AXIS));p2.add(p0);p2.add(p1);p2.add(slider);p2.add(Box.createGlue());JPanel p3 =new JPanel();p3.add(label2);p3.add(cmb);JPanel p4 = new JPanel();p4.add(label3);p4.add(button1);JPanel p5 = new JPanel();p5.setLayout(new BorderLayout());p5.add(p4,BorderLayout.CENTER);p5.add(tf2,BorderLayout.SOUTH);JPanel p6 = new JPanel ();p6.add(button2);//两个分割装3个部分JSplitPane s1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT,false, p2,p3);s1.setResizeWeight(0.7);//设置分隔权重s1.setOneTouchExpandable(false);//设置JSplitPane是否可以展开或收起(如同文件总管一般),设为true表示打开此功能。s1.setDividerSize(1);// 设置分隔线宽度的大小,以pixel为计算单位。JSplitPane s2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT,false, s1,p5);s2.setResizeWeight(0.8);s2.setOneTouchExpandable(false);s2.setDividerSize(1);JSplitPane s3 = new JSplitPane(JSplitPane.VERTICAL_SPLIT,false, s2,p6);s3.setResizeWeight(0.8);s3.setOneTouchExpandable(false);s3.setDividerSize(3);rightPanel.add(s3);}private void createLeftPanel() {leftPanel = new JPanel();BorderLayout layout = new BorderLayout();leftPanel.setLayout(layout);//所需组件:1label,1按钮,1列表框(1数组)JList<String> list = new JList<>();//list组件clearAllImg();list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);list.setListData(imgNameList);//依次添加panelimgPanel = new JPanel();//1.上方:图片显示区img = new ImageLabel(".\\src\\img\\pic3.png",400,250);imgPanel.add(img);//1.1为imgPanel添加拖拽动作处理new DropTarget(imgPanel, DnDConstants.ACTION_COPY_OR_MOVE,new DropTargetAdapter() {@Overridepublic void drop(DropTargetDropEvent dtde) {try {//1.如果拖入的文件受Java支持if (dtde.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {//1.1接受拖拽来的数据dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);//1.2对这些数据进行处理@SuppressWarnings("unchecked")List<File> files = (List<File>) dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);String[] fileTypes = new String[] {"png","PNG","jpg","JPG","gif","GIF","JPEG","jpeg","bmp","BMP"};//支持的文件类型(通过后缀判定。较为简单)String fileName;for(File file:files) {fileName = file.getName();if(checkFileType(fileName, fileTypes)) {msg.setText(addImgList(file.getAbsolutePath(),file.getName()));list.setListData(imgNameList);}else {msg.setText("所选文件类型不支持!!");}}setImgIndex(0);//1.3指示拖拽操作已经完成dtde.dropComplete(true);}//2.否则拒绝拖拽来的数据else {dtde.rejectDrop();msg.setText("所选文件类型不支持!!");}}catch(Exception e) {e.printStackTrace();}}});//1.2为imgPanel添加计时器,定期刷新图片timer = new Timer(interval, e -> updateImgPanel());timer.start();JPanel p2 = new JPanel(new BorderLayout());//2.中间左侧:图片列表区JScrollPane scrollPane = new JScrollPane();scrollPane.setViewportView(list);p2.add(scrollPane,BorderLayout.CENTER);//3.中间右侧,按钮区JButton clearButton = new JButton("重置已选");clearButton.addActionListener(e -> {clearAllImg();list.setListData(imgNameList);msg.setText("已重置图片列表~");});JButton button1 = new JButton("添加新图片");//buttonbutton1.addActionListener(arg0 -> {JFileChooser fc=new JFileChooser("C:\\");fc.setMultiSelectionEnabled(true);//设置可以多选fc.setAcceptAllFileFilterUsed(false);//设置 接受所有类型为falseFileNameExtensionFilter filter = new FileNameExtensionFilter("PNG and JPG images", "png", "jpg");//文件类型过滤器fc.addChoosableFileFilter(filter);int val=fc.showOpenDialog(null); //文件打开对话框if(val==JFileChooser.APPROVE_OPTION){//正常选择文件File[] files = fc.getSelectedFiles();for(File file:files) {msg.setText(addImgList(file.getAbsolutePath(),file.getName()));list.setListData(imgNameList);}setImgIndex(0);}else{//未正常选择文件,如选择取消按钮msg.setText("未选择文件");}});JPanel p3_1 = new JPanel();p3_1.setLayout(new BoxLayout(p3_1,BoxLayout.Y_AXIS));p3_1.add(Box.createVerticalStrut(20));p3_1.add(clearButton);p3_1.add(Box.createVerticalStrut(10));p3_1.add(button1);//左侧,右下角,按钮区JPanel buttonPanel = new JPanel();buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.Y_AXIS));buttonPanel.add(Box.createHorizontalStrut(20));buttonPanel.add(p3_1);JPanel p4 = new JPanel();//4.最下方:信息区p4.add(msg);//三个splitPane分割四个区域JSplitPane s1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,false, p2, buttonPanel);s1.setResizeWeight(0.67);//设置分隔权重s1.setOneTouchExpandable(true);//设置JSplitPane是否可以展开或收起(如同文件总管一般),设为true表示打开此功能。s1.setDividerSize(3);// 设置分隔线宽度的大小,以pixel为计算单位。JSplitPane s2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT,false, imgPanel,s1);s2.setResizeWeight(0.6);s2.setOneTouchExpandable(false);s2.setDividerSize(3);JSplitPane s3 = new JSplitPane(JSplitPane.VERTICAL_SPLIT,false, s2,p4);s3.setResizeWeight(0.8);s3.setOneTouchExpandable(false);s3.setDividerSize(0);leftPanel.add(s3,BorderLayout.CENTER);}protected void updateImgPanel() {imgPanel.removeAll();if(getImgNum() == 0) {//如果还未选择图片img = new ImageLabel(".\\src\\img\\pic3.png",400,250);}else {imgPanel.add(new ImageLabel(imgPathList[imgIndex],400,250));updateImgIndex();}imgPanel.add(img);//1.这个行//imgPanel.add(new ImageLabel("xxx",xx,xx));//2.但直接这个就不行??img.updateUI();}protected void updateImgIndex() {if(imgIndex + 1 >= getImgNum()) {setImgIndex(0);}else {setImgIndex(imgIndex+1);}}/*** @Description 在点击“生成gif”按钮后,判定是否满足条件* @Function UI4.java* @author LIAN* @date 2021年2月14日 上午11:49:41*/protected String checkConditions() {if(getImgNum()==0) {return "还未添加图片!";}if(!hasPath) {return "还未选择输出路径!";}return null;}protected boolean checkFileType(String fileName, String[] args) {String fileExtraName = fileName.substring(fileName.lastIndexOf(".")+1);for(String fileType : args) {if(fileType.equals(fileExtraName)) {return true;}}return false;}/*** @Description 清空已选图片列表* @author LIAN* @date 2021年2月17日 上午11:21:14*/public void clearAllImg() {setImgNum(0);setImgIndex(0);for (int i=0; i < maxNum; i++) {imgPathList[i]="  ";imgNameList[i]="  ";}}public String[] getImgList() {return imgPathList;}/*** @Function UI5.java* @Description 尝试添加一个选中的文件信息到已选String数组内,并返回添加信息* @return String--成功返回添加成功信息,失败则返回警告* @author LIAN* @date 2021年2月17日 上午11:22:21*/public String addImgList(String newImgPath,String newImgName) {int num = getImgNum();if(num < maxNum) {setImgNum(num+1);imgPathList[num] = newImgPath;imgNameList[num] = newImgName;return "成功添加第" + (num + 1) + "个图片";}elsereturn "警告:已经达到添加上限!";}public int getImgNum() {return imgNum;}public void setImgNum(int imgNum) {this.imgNum = imgNum;}public void setImgIndex(int imgIndex) {this.imgIndex = imgIndex;}public int getInterval(){return this.interval;}public void setInterval(int interval) {this.interval = interval;}}

4、个人总结

  • 首先是用时,快开学前的十几天开始写的,零零碎碎的总共大概花了40+h。勤快的时候一天七八个小时熬到半夜,懒得时候打开电脑发呆ing,总体还是挺满意的吧。

  • 其次是项目完成度,由于临近开学,时间上比较匆忙,距离我最开始的设想,还“差移除图片、调整顺序、输出图的文件大小(或尺寸,或清晰度)选择 ”这三项没搞,不过搞起来也不是很难。

TODO List:

  • 最后就是代码优化,UI部分写的一团乱麻。。以后的话,尽量还是分模块写,一小块一小块实现,最后再归一化,这样代码也比较清晰,不会显得太过臃肿。

    ps:Swing是真的垃圾,被弃用不是没有原因的。再写UI的话,估计也会使用其他UI工具。

5、参考资料

太多了,就列这几个主要的吧~

  1. Java根据图片生成GIF动图
  2. JavaFX 类 DirectoryChooser用法及代码示例
  3. 如何设置Icon的图片大小
  4. swing-窗体添加背景图片的2种方法
  5. 如何加载同一文件夹中的已有图片

6、exe文件下载

点击下载
提取码:twl6

(Java实现)图片合成GIF动图(“复古”Swing界面)相关推荐

  1. 怎么做GIF动画?怎样将图片合成gif动图

    大家平时看到的动态图片其实也是一种图片格式,只是由于与其他静态图片不同的是图片能动,所以有很多小伙伴以为gif是视频格式,其实gif动画我们也是可以自己制作的,通过把我们平时拍摄的照片进行合成就可以得 ...

  2. Java根据图片生成GIF动图

    昨天看到手机QQ空间可以预览自己手机上的图片并生成GIF图片,然后看到微信的公众号上很多都是动图,于是就想用java将几张图片生成gif图. 合成gif的图片大小最好一致,不要问我为什么. 具体代码如 ...

  3. 图片如何合成GIF?教你一键在线合成GIF动图

    GIF动图是现在常用的图片格式,比起普通的静态图片更加的生动丰富有趣.自然制作GIF动图的方法也有很多,比如最常见的图片合成GIF动图.那么,我们自己应该怎样将多张图片合成GIF动图呢?这时候,大家就 ...

  4. 动态图片怎么做?教你一键合成gif动图

    在浏览网页的时候看到一些搞笑有趣的图片,想要制作成gif动图的效果,但是不会使用PS的时候,应该如何制作呢?下面,小编为大家推荐一款简单实用的在线图片制作(https://www.yasuotu.co ...

  5. 图片怎么合成gif动图?操作步骤详解

    图片怎么合成gif动图?gif动图的使用非常的广泛,平时我们聊天中见到的动态表情,已经逛某宝时看到的动态广告图,其实都是gif动图.小编作为一名互联网从业者,经常需要使用或者制作gif动态图片.不知道 ...

  6. 静图怎样合成gif动图?仅需三步在线制作GIF动图

    当下gif动图非常受大众的欢迎,普通的静态图片已经无法满足人们的需求了.那么,作为一名小白,我们应该怎么才能将静态图片制作成gif动图呢?通过使用[GIF中文网]的gif制作(https://www. ...

  7. 如何使用gif制作软件快速合成gif动图....

    GIF动图是日常广泛使用的一种图片格式,在网上能够看到很多应用gif动图的场景,那么想要自己制作gif动图的话,该怎样来进行操作呢?接下来给大家分享一款gif制作软件(https://www.gif. ...

  8. java 处理图片图片合成

    最近写了一个java的图片合成相关的项目,真的是一踩一个坑,下面博主说说遇见的一些坑和怎么解决的这些问题. 本文主要讲图片合成,加文字还是比较简单的. 先讲下我的需求,把一张图片盖到另外一张图片上面, ...

  9. 怎样把多张图片快速合成GIF?如何在线合成gif动图?

    现在gif动图的使用场景非常的广泛,不仅在社交聊天中经常看到,在很多的宣传活动中也经常看到动图的展示,与静态图片相比,动图展现出的效果更好,视觉效果更具冲击性.那么gif动图怎么制作呢? 使用GIF中 ...

最新文章

  1. 在Ubuntu 14.04 64bit上查看硬件配置信息
  2. 大数据如何学习 cda认证_第十届CDA认证考试 LEVEL 1 优秀考生访问录:我是如何备考的?...
  3. beyond compare 4 的30天试用期已过-解决方法
  4. c dup 函数
  5. 2!=5 or 0在python中是否正确-python数据分析第二版:numpy
  6. TCP/IP之大明内阁---协议的制定
  7. 富士施乐服务器系统安装,富士施乐DocuCentre-IV C2265打印机安装教程
  8. mat-form-field must contain a MatFormFieldControl错误的解决方法
  9. Oracle 学习笔记(三)
  10. python vars name报错_Python vars()全局名称错误
  11. 软件导航html单页源码
  12. Android ViewStub
  13. css居中最佳方案,CSS 水平、垂直居中的5种最佳方案
  14. tomcat的服务器目录在哪个文件夹,Tomcat目录结构详细介绍
  15. Redis 基础:Redis 配置
  16. java项目描述_111个知名Java项目集锦(包括url和描述)
  17. sun cluster 3.3 +oracle 10g R2 RAC with ASM on solaris 10 U9
  18. 软件促进两化深度融合 ——记2016中国软件和信息技术服务业发展高峰论坛
  19. DataV 你值得拥有的大屏展示工具
  20. 以 2、3……16 进制输出一无符号整型数

热门文章

  1. mysql设计学习_mySQL学习入门教程——3.SQL的设计和编写
  2. Win32位程序设计初步之系统安全
  3. rtrim() 函数
  4. CentOS7磁盘分区及文件系统
  5. 邮件协议SMTP、POP3和IMAP
  6. 微信打造智能家居新生活
  7. 免费金融行情数据集——Tushare数据调取最简教程
  8. 使用C#调用PI-SDK进行基于PI的开发——使用PI-SDK建立与PI数据库的连接(转载)
  9. c语言是低级的高级程序设计语言,计算机高级程序设计(C语言)
  10. kali2022安装配置、换源、输入法安装