More importantly... could I, say, prepare and link to a payload that would inject Java code into each client within render distance of the picture, too?
... just askin'.
Because if I were able to do that, I don't think that'd be a good thing.
More importantly... could I, say, prepare and link to a payload that would inject Java code into each client within render distance of the picture, too?
... just askin'.
Because if I were able to do that, I don't think that'd be a good thing.
No you shouldn't be able to do that the only thing you will be able to see is a small error appear your the log file.
DIY GIF Animation(Break up for each frame, use GifDecoder getframe and get delays)
import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.image.*;
public class GIFDecoder {
/**
* File read status: No errors.
*/
public static final int STATUS_OK = 0;
/**
* File read status: Error decoding file (may be partially decoded)
*/
public static final int STATUS_FORMAT_ERROR = 1;
/**
* File read status: Unable to open source.
*/
public static final int STATUS_OPEN_ERROR = 2;
protected BufferedInputStream in;
protected int status;
protected int width; // full image width
protected int height; // full image height
protected boolean gctFlag; // global color table used
protected int gctSize; // size of global color table
protected int loopCount = 1; // iterations; 0 = repeat forever
protected int[] gct; // global color table
protected int[] lct; // local color table
protected int[] act; // active color table
protected int bgIndex; // background color index
protected int bgColor; // background color
protected int lastBgColor; // previous bg color
protected int pixelAspect; // pixel aspect ratio
protected boolean lctFlag; // local color table flag
protected boolean interlace; // interlace flag
protected int lctSize; // local color table size
protected int ix, iy, iw, ih; // current image rectangle
protected Rectangle lastRect; // last image rect
protected BufferedImage image; // current frame
protected BufferedImage lastImage; // previous frame
protected byte[] block = new byte[256]; // current data block
protected int blockSize = 0; // block size
// last graphic control extension info
protected int dispose = 0;
// 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
protected int lastDispose = 0;
protected boolean transparency = false; // use transparent color
protected int delay = 0; // delay in milliseconds
protected int transIndex; // transparent color index
protected static final int MaxStackSize = 4096;
// max decoder pixel stack size
// LZW decoder working arrays
protected short[] prefix;
protected byte[] suffix;
protected byte[] pixelStack;
protected byte[] pixels;
protected ArrayList frames; // frames read from current file
protected int frameCount;
static class GifFrame {
public GifFrame(BufferedImage im, int del) {
image = im;
delay = del;
}
public BufferedImage image;
public int delay;
}
/**
* Gets display duration for specified frame.
*
* @param n int index of frame
* @return delay in milliseconds
*/
public int getDelay(int n) {
//
delay = -1;
if ((n >= 0) && (n < frameCount)) {
delay = ((GifFrame) frames.get(n)).delay;
}
return delay;
}
/**
* Gets the number of frames read from file.
* @return frame count
*/
public int getFrameCount() {
return frameCount;
}
/**
* Gets the first (or only) image read.
*
* @return BufferedImage containing first frame, or null if none.
*/
public BufferedImage getImage() {
return getFrame(0);
}
/**
* Gets the "Netscape" iteration count, if any.
* A count of 0 means repeat indefinitiely.
*
* @return iteration count if one was specified, else 1.
*/
public int getLoopCount() {
return loopCount;
}
/**
* Creates new frame image from current data (and previous
* frames as specified by their disposition codes).
*/
protected void setPixels() {
// expose destination image's pixels as int array
int[] dest =
((DataBufferInt) image.getRaster().getDataBuffer()).getData();
// fill in starting image contents based on last image's dispose code
if (lastDispose > 0) {
if (lastDispose == 3) {
// use image before last
int n = frameCount - 2;
if (n > 0) {
lastImage = getFrame(n - 1);
} else {
lastImage = null;
}
}
if (lastImage != null) {
int[] prev =
((DataBufferInt) lastImage.getRaster().getDataBuffer()).getData();
System.arraycopy(prev, 0, dest, 0, width * height);
// copy pixels
if (lastDispose == 2) {
// fill last image rect area with background color
Graphics2D g = image.createGraphics();
Color c = null;
if (transparency) {
c = new Color(0, 0, 0, 0); // assume background is transparent
} else {
c = new Color(lastBgColor); // use given background color
}
g.setColor(c);
g.setComposite(AlphaComposite.Src); // replace area
g.fill(lastRect);
g.dispose();
}
}
}
// copy each source line to the appropriate place in the destination
int pass = 1;
int inc = 8;
int iline = 0;
for (int i = 0; i < ih; i++) {
int line = i;
if (interlace) {
if (iline >= ih) {
pass++;
switch (pass) {
case 2 :
iline = 4;
break;
case 3 :
iline = 2;
inc = 4;
break;
case 4 :
iline = 1;
inc = 2;
}
}
line = iline;
iline += inc;
}
line += iy;
if (line < height) {
int k = line * width;
int dx = k + ix; // start of line in dest
int dlim = dx + iw; // end of dest line
if ((k + width) < dlim) {
dlim = k + width; // past dest edge
}
int sx = i * iw; // start of line in source
while (dx < dlim) {
// map color and insert in destination
int index = ((int) pixels[sx++]) & 0xff;
int c = act[index];
if (c != 0) {
dest[dx] = c;
}
dx++;
}
}
}
}
/**
* Gets the image contents of frame n.
*
* @return BufferedImage representation of frame, or null if n is invalid.
*/
public BufferedImage getFrame(int n) {
BufferedImage im = null;
if ((n >= 0) && (n < frameCount)) {
im = ((GifFrame) frames.get(n)).image;
}
return im;
}
/**
* Gets image size.
*
* @return GIF image dimensions
*/
public Dimension getFrameSize() {
return new Dimension(width, height);
}
/**
* Reads GIF image from stream
*
* @param BufferedInputStream containing GIF file.
* @return read status code (0 = no errors)
*/
public int read(BufferedInputStream is) {
init();
if (is != null) {
in = is;
readHeader();
if (!err()) {
readContents();
if (frameCount < 0) {
status = STATUS_FORMAT_ERROR;
}
}
} else {
status = STATUS_OPEN_ERROR;
}
try {
is.close();
} catch (IOException e) {
}
return status;
}
/**
* Reads GIF image from stream
*
* @param InputStream containing GIF file.
* @return read status code (0 = no errors)
*/
public int read(InputStream is) {
init();
if (is != null) {
if (!(is instanceof BufferedInputStream))
is = new BufferedInputStream(is);
in = (BufferedInputStream) is;
readHeader();
if (!err()) {
readContents();
if (frameCount < 0) {
status = STATUS_FORMAT_ERROR;
}
}
} else {
status = STATUS_OPEN_ERROR;
}
try {
is.close();
} catch (IOException e) {
}
return status;
}
/**
* Reads GIF file from specified file/URL source
* (URL assumed if name contains ":/" or "file:")
*
* @param name String containing source
* @return read status code (0 = no errors)
*/
public int read(String name) {
status = STATUS_OK;
try {
name = name.trim().toLowerCase();
if ((name.indexOf("file:") >= 0) ||
(name.indexOf(":/") > 0)) {
URL url = new URL(name);
in = new BufferedInputStream(url.openStream());
} else {
in = new BufferedInputStream(new FileInputStream(name));
}
status = read(in);
} catch (IOException e) {
status = STATUS_OPEN_ERROR;
}
return status;
}
/**
* Decodes LZW image data into pixel array.
* Adapted from John Cristy's ImageMagick.
*/
protected void decodeImageData() {
int NullCode = -1;
int npix = iw * ih;
int available,
clear,
code_mask,
code_size,
end_of_information,
in_code,
old_code,
bits,
code,
count,
i,
datum,
data_size,
first,
top,
bi,
pi;
if ((pixels == null) || (pixels.length < npix)) {
pixels = new byte[npix]; // allocate new pixel array
}
if (prefix == null) prefix = new short[MaxStackSize];
if (suffix == null) suffix = new byte[MaxStackSize];
if (pixelStack == null) pixelStack = new byte[MaxStackSize + 1];
// Initialize GIF data stream decoder.
data_size = read();
clear = 1 << data_size;
end_of_information = clear + 1;
available = clear + 2;
old_code = NullCode;
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
for (code = 0; code < clear; code++) {
prefix = 0;
suffix = (byte) code;
}
// Decode GIF pixel stream.
datum = bits = count = first = top = pi = bi = 0;
for (i = 0; i < npix;) {
if (top == 0) {
if (bits < code_size) {
// Load bytes until there are enough bits for a code.
if (count == 0) {
// Read a new data block.
count = readBlock();
if (count <= 0)
break;
bi = 0;
}
datum += (((int) block[bi]) & 0xff) << bits;
bits += 8;
bi++;
count--;
continue;
}
// Get the next code.
code = datum & code_mask;
datum >>= code_size;
bits -= code_size;
// Interpret the code
if ((code > available) || (code == end_of_information))
break;
if (code == clear) {
// Reset decoder.
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
available = clear + 2;
old_code = NullCode;
continue;
}
if (old_code == NullCode) {
pixelStack[top++] = suffix;
old_code = code;
first = code;
continue;
}
in_code = code;
if (code == available) {
pixelStack[top++] = (byte) first;
code = old_code;
}
while (code > clear) {
pixelStack[top++] = suffix;
code = prefix;
}
first = ((int) suffix) & 0xff;
// Add a new string to the string table,
if (available >= MaxStackSize)
break;
pixelStack[top++] = (byte) first;
prefix[available] = (short) old_code;
suffix[available] = (byte) first;
available++;
if (((available & code_mask) == 0)
&& (available < MaxStackSize)) {
code_size++;
code_mask += available;
}
old_code = in_code;
}
// Pop a pixel off the pixel stack.
top--;
pixels[pi++] = pixelStack[top];
i++;
}
for (i = pi; i < npix; i++) {
pixels[i] = 0; // clear missing pixels
}
}
/**
* Returns true if an error was encountered during reading/decoding
*/
protected boolean err() {
return status != STATUS_OK;
}
/**
* Initializes or re-initializes reader
*/
protected void init() {
status = STATUS_OK;
frameCount = 0;
frames = new ArrayList();
gct = null;
lct = null;
}
/**
* Reads a single byte from the input stream.
*/
protected int read() {
int curByte = 0;
try {
curByte = in.read();
} catch (IOException e) {
status = STATUS_FORMAT_ERROR;
}
return curByte;
}
/**
* Reads next variable length block from input.
*
* @return number of bytes stored in "buffer"
*/
protected int readBlock() {
blockSize = read();
int n = 0;
if (blockSize > 0) {
try {
int count = 0;
while (n < blockSize) {
count = in.read(block, n, blockSize - n);
if (count == -1)
break;
n += count;
}
} catch (IOException e) {
}
if (n < blockSize) {
status = STATUS_FORMAT_ERROR;
}
}
return n;
}
/**
* Reads color table as 256 RGB integer values
*
* @param ncolors int number of colors to read
* @return int array containing 256 colors (packed ARGB with full alpha)
*/
protected int[] readColorTable(int ncolors) {
int nbytes = 3 * ncolors;
int[] tab = null;
byte[] c = new byte[nbytes];
int n = 0;
try {
n = in.read(c);
} catch (IOException e) {
}
if (n < nbytes) {
status = STATUS_FORMAT_ERROR;
} else {
tab = new int[256]; // max size to avoid bounds checks
int i = 0;
int j = 0;
while (i < ncolors) {
int r = ((int) c[j++]) & 0xff;
int g = ((int) c[j++]) & 0xff;
int b = ((int) c[j++]) & 0xff;
tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
return tab;
}
/**
* Main file parser. Reads GIF content blocks.
*/
protected void readContents() {
// read GIF file content blocks
boolean done = false;
while (!(done || err())) {
int code = read();
switch (code) {
case 0x2C : // image separator
readImage();
break;
case 0x21 : // extension
code = read();
switch (code) {
case 0xf9 : // graphics control extension
readGraphicControlExt();
break;
case 0xff : // application extension
readBlock();
String app = "";
for (int i = 0; i < 11; i++) {
app += (char) block[i];
}
if (app.equals("NETSCAPE2.0")) {
readNetscapeExt();
}
else
skip(); // don't care
break;
default : // uninteresting extension
skip();
}
break;
case 0x3b : // terminator
done = true;
break;
case 0x00 : // bad byte, but keep going and see what happens
break;
default :
status = STATUS_FORMAT_ERROR;
}
}
}
/**
* Reads Graphics Control Extension values
*/
protected void readGraphicControlExt() {
read(); // block size
int packed = read(); // packed fields
dispose = (packed & 0x1c) >> 2; // disposal method
if (dispose == 0) {
dispose = 1; // elect to keep old image if discretionary
}
transparency = (packed & 1) != 0;
delay = readShort() * 10; // delay in milliseconds
transIndex = read(); // transparent color index
read(); // block terminator
}
/**
* Reads GIF file header information.
*/
protected void readHeader() {
String id = "";
for (int i = 0; i < 6; i++) {
id += (char) read();
}
if (!id.startsWith("GIF")) {
status = STATUS_FORMAT_ERROR;
return;
}
readLSD();
if (gctFlag && !err()) {
gct = readColorTable(gctSize);
bgColor = gct[bgIndex];
}
}
/**
* Reads next frame image
*/
protected void readImage() {
ix = readShort(); // (sub)image position & size
iy = readShort();
iw = readShort();
ih = readShort();
int packed = read();
lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
interlace = (packed & 0x40) != 0; // 2 - interlace flag
// 3 - sort flag
// 4-5 - reserved
lctSize = 2 << (packed & 7); // 6-8 - local color table size
if (lctFlag) {
lct = readColorTable(lctSize); // read table
act = lct; // make local table active
} else {
act = gct; // make global table active
if (bgIndex == transIndex)
bgColor = 0;
}
int save = 0;
if (transparency) {
save = act[transIndex];
act[transIndex] = 0; // set transparent color if specified
}
if (act == null) {
status = STATUS_FORMAT_ERROR; // no color table defined
}
if (err()) return;
decodeImageData(); // decode pixel data
skip();
if (err()) return;
frameCount++;
// create new image to receive frame data
image =
new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
setPixels(); // transfer pixel data to image
frames.add(new GifFrame(image, delay)); // add image to frame list
if (transparency) {
act[transIndex] = save;
}
resetFrame();
}
/**
* Reads Logical Screen Descriptor
*/
protected void readLSD() {
// logical screen size
width = readShort();
height = readShort();
// packed fields
int packed = read();
gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
// 2-4 : color resolution
// 5 : gct sort flag
gctSize = 2 << (packed & 7); // 6-8 : gct size
bgIndex = read(); // background color index
pixelAspect = read(); // pixel aspect ratio
}
/**
* Reads Netscape extenstion to obtain iteration count
*/
protected void readNetscapeExt() {
do {
readBlock();
if (block[0] == 1) {
// loop count sub-block
int b1 = ((int) block[1]) & 0xff;
int b2 = ((int) block[2]) & 0xff;
loopCount = (b2 << 8) | b1;
}
} while ((blockSize > 0) && !err());
}
/**
* Reads next 16-bit value, LSB first
*/
protected int readShort() {
// read 16-bit value, LSB first
return read() | (read() << 8);
}
/**
* Resets frame state for reading next image.
*/
protected void resetFrame() {
lastDispose = dispose;
lastRect = new Rectangle(ix, iy, iw, ih);
lastImage = image;
lastBgColor = bgColor;
int dispose = 0;
boolean transparency = false;
int delay = 0;
lct = null;
}
protected void skip() {
do {
readBlock();
} while ((blockSize > 0) && !err());
}
}
lol~~~
Only in the binding texture map there are some lag
At the time of playing will not drop FPS
and I added some features:
Because when loaded images,the texture permanent take up GPU memory
If you look at too many GIF or High resolution image,takes up too much GPU memory
so i added feature:As the picture too long are not you look at it
DeleteTexture and cache to file(when Close the client delete the cache)
The next time the loading pictures,Only need extracted from local catch
My guess is, it sees the .GIF ending and expects multiple frames to animate, but there is just one static frame. Which is unexpected and crashes everyone within render distance until the block is broken.
May I suggest adding a server console thing to enable / disable all picture frames? Like, oops someones picture frame crashes every client in the radius of its render distance, let's just execute a command on server console or from ingame [if you're outside the affected range and have time to do so] to basically keep all the settings and links in there, but disable the displaying of pictures so you only see the wooden frames to go and fix stuff.
My guess is, it sees the .GIF ending and expects multiple frames to animate, but there is just one static frame. Which is unexpected and crashes everyone within render distance until the block is broken.
May I suggest adding a server console thing to enable / disable all picture frames? Like, oops someones picture frame crashes every client in the radius of its render distance, let's just execute a command on server console or from ingame [if you're outside the affected range and have time to do so] to basically keep all the settings and links in there, but disable the displaying of pictures so you only see the wooden frames to go and fix stuff.
Fixed it, thanks for reporting. Update should be available soon.
First off I would like to thank you for this mod. As it is now, it is a huge help for my server. It allows greater creativity, better signage, and improved aesthetics.
That said, I have a couple suggestions/feature requests.
1. Redstone #1. Could you have an option for the picture frame to react to a redstone signal? I:E redstone signal detected the frame turns off etc..
I think some interesting builds could be done with this.
2. Redstone #2. If you can activate it with redstone, would it be possible to use variable strength redstone to activate multiple images?
I:E The # for the picture would correspond to the # of the redstone signal strength.
This could be used to make dynamic signage, as well as other interesting builds.
3. No repeat. If a redstone signal can turn on/off the picture frame, how about allowing the picture frame to play an animated gif once?
That way an animated message can play once when a button is pressed. This would allow you to have animated instructions that play on demand, instead of a player catching the animation randomly in the middle somewhere.
4. Redstone #3. If Request #3 works, how about the idea of the picture frame sending out it's own redstone signal when the animated gif ends?
Imagine a series of animated gifs going off one by one activated in turn by the preceding one. You could do things like animated/automated gif tours with text, arrows, or if your skilled enough an animated character. It would be awesome.
5. An optional transparency slider? Yes this would be useful.
6. I suppose adding video support would be too hard?
7. Maybe an animated radio block? supports online mp3s or radio streams?
There used to be a mod that added streaming radio, but I can't find something that works for this version of minecraft.
(I know I'm pushing it)
Yeah these suggestions are probably annoying. Still, I had to try
Anyway, thank you again for the mod, it is greatly appreciated.
Released v1.2.3
Loving this mod. I would suggest making the online picture frames drop themselves when broken. Also what is the blockid for the picture frames?
Cubic Chunks Suggestion! (Link in text mode because banner image is 404) Cubic Chunks Mod below:
Should be fixed in the newest version (1.2.4). The "id" of the frame is "opframe:opframe".
Thanks! Grabbed the new version.
Cubic Chunks Suggestion! (Link in text mode because banner image is 404) Cubic Chunks Mod below:
Neat! Sooo... how does this work, exactly?
More importantly... could I, say, prepare and link to a payload that would inject Java code into each client within render distance of the picture, too?
... just askin'.
Because if I were able to do that, I don't think that'd be a good thing.
No you shouldn't be able to do that the only thing you will be able to see is a small error appear your the log file.
DIY GIF Animation(Break up for each frame, use GifDecoder getframe and get delays)
lol~~~
Only in the binding texture map there are some lag
At the time of playing will not drop FPS
and I added some features:
Because when loaded images,the texture permanent take up GPU memory
If you look at too many GIF or High resolution image,takes up too much GPU memory
so i added feature:As the picture too long are not you look at it
DeleteTexture and cache to file(when Close the client delete the cache)
The next time the loading pictures,Only need extracted from local catch
I hope it can help you develop the GIF support
WOW, i just don't know what to say. Thank you for spending time to help out
Unfortunately i ran into some syntax errors, i could try to fix them, but actually it's kinda difficult to find out what it should do:
prefix is a short[], while suffix is a byte[], so is there an index missing? And there are some other ones as well (same issue):
Maybe you know how to fix it?
Use this one↑↑↑↑(i don't know why use the code block was change some code)
and use it
GifDecoder decoder = new GifDecoder();
decoder.read(url);
.....
Thank you sooo much. It's implement:
If you are interested this is basically how i implemented it:
https://github.com/CreativeMD/OnlinePictureFrame/blob/1.11.2/src/main/java/com/creativemd/opf/client/DownloadThread.java#L148
https://github.com/CreativeMD/OnlinePictureFrame/blob/1.11.2/src/main/java/com/creativemd/opf/client/AnimatedPictureTexture.java
Loading performance might not be the best, but i guess it's alright. Again thanks for spending time and sharing your code. Will release it soon.
I think The future can develop to play Flash,
Such as watching “youtube” with friends in the game
or can develop a Camera Mod
Hello, can you give the source code for version 1.7.10?
I think youtube videos are more complicated but a camera would be a neat idea.
https://github.com/CreativeMD/OnlinePictureFrame/tree/master
Woop! It crashes the game.
Try adding this one...
[ https://mmomechanicallyinclined.files.wordpress.com/2011/06/privateer1.gif ]
... to your game.
My guess is, it sees the .GIF ending and expects multiple frames to animate, but there is just one static frame. Which is unexpected and crashes everyone within render distance until the block is broken.
May I suggest adding a server console thing to enable / disable all picture frames? Like, oops someones picture frame crashes every client in the radius of its render distance, let's just execute a command on server console or from ingame [if you're outside the affected range and have time to do so] to basically keep all the settings and links in there, but disable the displaying of pictures so you only see the wooden frames to go and fix stuff.
Fixed it, thanks for reporting. Update should be available soon.
BlockPicFrame.java
line 217
Is not correct
that cant invisible frame
should do this
↓↓↓↓↓↓↓↓
Thanks for reporting. Fixed it, update should be available soon.
how to install the mod?
Download the zip file, drag it in your mods folder and you are done.
First off I would like to thank you for this mod. As it is now, it is a huge help for my server. It allows greater creativity, better signage, and improved aesthetics.
That said, I have a couple suggestions/feature requests.
1. Redstone #1. Could you have an option for the picture frame to react to a redstone signal? I:E redstone signal detected the frame turns off etc..
I think some interesting builds could be done with this.
2. Redstone #2. If you can activate it with redstone, would it be possible to use variable strength redstone to activate multiple images?
I:E The # for the picture would correspond to the # of the redstone signal strength.
This could be used to make dynamic signage, as well as other interesting builds.
3. No repeat. If a redstone signal can turn on/off the picture frame, how about allowing the picture frame to play an animated gif once?
That way an animated message can play once when a button is pressed. This would allow you to have animated instructions that play on demand, instead of a player catching the animation randomly in the middle somewhere.
4. Redstone #3. If Request #3 works, how about the idea of the picture frame sending out it's own redstone signal when the animated gif ends?
Imagine a series of animated gifs going off one by one activated in turn by the preceding one. You could do things like animated/automated gif tours with text, arrows, or if your skilled enough an animated character. It would be awesome.
5. An optional transparency slider? Yes this would be useful.
6. I suppose adding video support would be too hard?
7. Maybe an animated radio block? supports online mp3s or radio streams?
There used to be a mod that added streaming radio, but I can't find something that works for this version of minecraft.
(I know I'm pushing it)
Yeah these suggestions are probably annoying. Still, I had to try
Anyway, thank you again for the mod, it is greatly appreciated.