/* * Copyright (c) 1984, 1988, 1990, 1993-1995 by Rick Jansen, * all rights reserved. * Copyright (c) 1995 by Jan Andersson, Torpa Konsult AB. * * Permission to use, copy, and distribute this software for * NON-COMMERCIAL purposes and without fee is hereby granted * provided that this copyright notice appears in all copies. * * All rights of "look and feel" and bitmaps used are retained by * Rick Jansen. * * Original Xsnow Copyright text: * * Xsnow is available freely and you may give it to other people as is, * but I retain all rights. Therefore it does not classify as 'Public * Domain' software. * */ import java.applet.*; import java.awt.*; import java.awt.image.*; import java.util.*; /** * ChristmasCard - Christmas Card Applet * * Inspired by Rick Jansen's Xsnow. * * @version 1.4, 12/07/95 * @author Jan Andersson (janne@torpa.se) * */ public class ChristmasCard extends Applet implements Runnable { Thread thread = null; // animation thread boolean suspended = false; // animation thread suspended Vector snowFlakes = null; // animated snow flakes int noOfFlakes; // number of snow flakes Santa santa = null; // animated Santa MovingText text = null; // animated text int lastWidth = 0; // last know width int lastHeight = 0; // last know height Frame aboutFrame = null; // About popup frame Button aboutButton = null; // button to popup About frame boolean showAbout = true; // true if About button to be used boolean restarted = true; // true if re-started // default text lines String msg1 = "Merry Christmas"; String msg2 = "And"; String msg3 = "A Happy New Year!"; String msg4 = "From Janne Andersson"; // updates done "off-screen" private Image offScreenImage; private Graphics offScreenGraphics; private Dimension offScreenSize; public void init() { String par = getParameter("msg1"); if (par != null) msg1 = par; par = getParameter("msg2"); if (par != null) msg2 = par; par = getParameter("msg3"); if (par != null) msg3 = par; par = getParameter("msg4"); if (par != null) msg4 = par; par = getParameter("flakes"); noOfFlakes = (par == null) ? 30 : Integer.valueOf(par).intValue(); Color sky = readColor(getParameter("sky"), Color.blue); Color snow = readColor(getParameter("snow"), Color.white); // use sky as background as well and red as foreground setBackground(sky); setForeground(Color.red); // init statics for moving components SmallSnowFlake.initStatic(this, snow, sky); BigSnowFlake.initStatic(this, snow, sky); Santa.initStatic(this, sky); Font font = new Font("TimesRoman", Font.BOLD, 22); MovingText.initStatic(msg1, msg2, msg3, msg4, font, Color.red); if (showAbout && aboutButton == null) { // create "About" button aboutButton = new Button("About..."); //aboutButton.setBackground(sky); //aboutButton.setForeground(snow); add(aboutButton); } } /** * Applet Info. */ public String getAppletInfo() { return "ChristmasCard.java 1.4, by janne@torpa.se"; } /** * Parameter Info. */ public String[][] getParameterInfo() { // More should be added... String[][] info = { {"msg1", "string", "text 1 (Merry Christmas)"}, {"msg2", "string", "text 2 (And)"}, {"msg3", "string", "text 3 (A Happy New Year!)"}, {"msg4", "string", "text 4 (From Janne Andersson!)"}, {"snow", "string", "Snow Color (White)"}, {"sky", "string", "Sky Color (Blue)"}, {"flakes", "int", "Number of snow flakes (30)"}, }; return info; } /** * Convert a Hexadecimal String with RGB-Values to Color * Uses aDefault, if no or not enough RGB-Values */ public Color readColor(String aColor, Color aDefault) { if ((aColor == null) || (aColor.charAt(0) != '#') || (aColor.length() != 7 )) { return aDefault; } try { Integer rgbValue = new Integer(0); rgbValue = Integer.valueOf(aColor.substring(1, 7), 16); return new Color(rgbValue.intValue()); } catch (Exception e) { return aDefault; } } /* * Create snow flakes */ public void createSnowFlakes() { if (snowFlakes == null) snowFlakes = new Vector(noOfFlakes); else snowFlakes.removeAllElements(); for (int i=0; i maxText) textIndex = 0; String newText; // when textIndex is max, we don't define any text (uses an // empty string), since we use a delay here if (textIndex >= maxText) newText = ""; else newText = text[textIndex]; textLength = newText.length(); chars = new char[textLength]; newText.getChars(0, textLength, chars, 0); xPos = new int[textLength]; yPos = new int[textLength]; for (int i = 0; i < textLength; i++) { xPos[i] = fm.charsWidth(chars, 0, i); yPos[i] = fm.getAscent() - 1; } dx = 2; dy = 2; int newWidth = dx * 2 + fm.stringWidth(newText); int newHeight = dy * 2 + font.getSize(); int newX = (width - newWidth)/2; int newY = (height - newHeight)/2; initSize(newWidth, newHeight); initPos(newX, newY, true); } void move(int width, int height) { count++; if (state == DROPPING) { // make characters drop down for (int i = 0; i < textLength; i++) { yPos[i] = yPos[i] + count; if (y+yPos[i] > height) { // we are out of view; break count = maxCount; break; } } } if (state == EXPLODING) { // make characters move left/right int mid = textLength/2; int outOfView = 0; for (int i = 0; i < textLength; i++) { if (i < mid) xPos[i] = xPos[i] - 3*(mid - i); else xPos[i] = xPos[i] + 3*(i - mid); if (x+xPos[i] > width || x+xPos[i] < 0) outOfView++; } if (outOfView > textLength-6) // we are out of view; break count = maxCount; } if (count >= maxCount) { // swich state and/or text if ((textIndex == 0 || textIndex == 2) && state == NORMAL) { // use fancy animation for text 1 and 3 double rand = Math.random(); if (rand < 0.5) state = EXPLODING; else state = DROPPING; } else { // switch to new text initText(textIndex+1, width, height); textState = TEXT_NORMAL; state = NORMAL; if (textIndex == 2) // text 3 displayed as "nervous" textState = TEXT_NERVOUS; else if (textIndex == maxText) state = DELAY; } // reset count if (state == DELAY || textIndex == 1) // but don't show delay and text 2 as long.. count = maxCount/2; else count = 0; } } void paint(Graphics g) { g.setFont(font); g.setColor(fgColor); for (int i = 0; i < textLength; i++) { int tmpX; int tmpY; if (textState == TEXT_NERVOUS) { // Nervous text tmpX = (int)(Math.random() * dx * 2) + xPos[i]; tmpY = (int)(Math.random() * dy * 2) + yPos[i]; } else { // Normal text tmpX = xPos[i]; tmpY = yPos[i]; } g.drawChars(chars, i, 1, x+tmpX, y+tmpY); } } // dummy; not used here Image image() { return null; } } /** * The Santa and his reins. */ class Santa extends MovingGraphComponent { private int dx; private int dy; private Image image; private int imageCount; static private BitMap bitMap; static private Image images[]; /** * static initializer */ static void initStatic(Component c, Color bg) { bitMap = SantaBitmap.instance(bg); int pixmap1[] = bitMap.getPixmap(0); int pixmap2[] = bitMap.getPixmap(1); int pixmap3[] = bitMap.getPixmap(2); // create images images = new Image[3]; images[0] = c.createImage(new MemoryImageSource( bitMap.width(), bitMap.height(), pixmap1, 0, bitMap.width())); images[1] = c.createImage(new MemoryImageSource( bitMap.width(), bitMap.height(), pixmap2, 0, bitMap.width())); images[2] = c.createImage(new MemoryImageSource( bitMap.width(), bitMap.height(), pixmap3, 0, bitMap.width())); } Santa(int width, int height) { imageCount = 0; image = images[0]; initSize(bitMap.width(), bitMap.height()); initStartPos(width, height); dx = 3; dy = 1; } void initStartPos(int width, int height) { int yRand = 20 + (int) (Math.random() * (double) (height/2 - 30)); if (Math.random() > 0.5) { // top half of screen yRand += height/2; } initPos(-w, yRand, true); } void move(int width, int height) { x += dx; if (x > width) { // restart santa initStartPos(width, height); } int newY = (Math.random() > 0.5) ? y+dy : y-dy; initPos(x, newY, true); image = images[imageCount++]; if (imageCount > 2) imageCount = 0; } Image image() { return image; } } /** * Abstract snow flake class */ abstract class SnowFlake extends MovingGraphComponent { private int dy; private int dx; private int maxDx; private boolean moveToTheRight; private int dirChangeSteps = 0; protected SnowFlake(int width, int height) { // spread the flakes around int xRand = (int) (Math.random() * (double) height); int yRand = (int) (Math.random() * (double) width); // only show the flakes a the top boolean visibleRand = yRand < height/5; initPos(xRand, yRand, visibleRand); // init dx and dy initDxDy(width, height); } void move(int width, int height) { if (Math.abs(dx) > dy) { // we are moving to much in x direction; make sure // we move in a direction to fix this up... if (moveToTheRight && dx > 0) moveToTheRight = false; else moveToTheRight = true; } else { // make the flakes change direction randomly if (Math.random() > 0.5) { // change direction moveToTheRight = !moveToTheRight; } } // also verify that we are withing the flakes own maximum // horizontal movement. if (dx > maxDx) moveToTheRight = false; if (dx < -maxDx) moveToTheRight = true; if (moveToTheRight) dx = dx + (int) (Math.random() * 2); else dx = dx - (int) (Math.random() * 2); x += dx; y += dy; // if out of view (left or right) make object in visible if (x < 0 || x > width) visible = false; if (y > height) { // restart at the top int xRand = (int) (Math.random() * (double) width); initPos(xRand, 0, true); // re-init dx and dy initDxDy(width, height); } } public void initDxDy(int width, int height) { int maxDy = Math.min(height/40, 4); maxDx = Math.min(width/80, 2); dx = (int) (Math.random() * maxDx-1) + 1; if (Math.random() > 0.5) dx = -dx; if (dx > 0) moveToTheRight = true; dy = (int) (Math.random() * (double) maxDy) + 1; } } /** * Small snow flake */ class SmallSnowFlake extends SnowFlake { static private BitMap bitMap; static private Image image; /** * static initializer */ static void initStatic(Component c, Color fg, Color bg) { bitMap = SmallSnowFlakeBitmap.instance(fg, bg); int pixmap[] = bitMap.getPixmap(); image = c.createImage(new MemoryImageSource( bitMap.width(), bitMap.height(), pixmap, 0, bitMap.width())); } SmallSnowFlake(int width, int height) { super(width, height); initSize(bitMap.width(), bitMap.height()); } Image image() { return image; } } /** * Bigger snow flake */ class BigSnowFlake extends SnowFlake { static private Image image; static private BitMap bitMap; /** * static initializer */ static void initStatic(Component c, Color fg, Color bg) { bitMap = LargeSnowFlakeBitmap.instance(fg, bg); int pixmap[] = bitMap.getPixmap(); image = c.createImage(new MemoryImageSource( bitMap.width(), bitMap.height(), pixmap, 0, bitMap.width())); } BigSnowFlake(int width, int height) { super(width, height); initSize(bitMap.width(), bitMap.height()); } Image image() { return image; } } /** * Abstract Bitmap class. * * Subclasses implements conversion of bitmaps (X11 style) to * color pixmaps that can be used to create images. */ abstract class BitMap { protected int width; protected int height; abstract public int[] getPixmap(); abstract public int[] getPixmap(int index); public int width() { return width; } public int height() { return height; } } class SmallSnowFlakeBitmap extends BitMap { static private BitMap instance = null; static private int bitmap[] = { 0x05, 0x02, 0x05 }; static private int pixmap[] = null; private SmallSnowFlakeBitmap(Color fg, Color bg) { width = height = 3; pixmap = new int[width*height]; int index = 0; for (int i=0; i