Initial commit

This commit is contained in:
Iván Delgado 2021-02-02 23:23:26 +01:00
commit 69831530c1
173 changed files with 24901 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
build
dist
compile.sh
.vscode

4
META-INF/MANIFEST.MF Normal file
View file

@ -0,0 +1,4 @@
Manifest-Version: 1.0
Class-Path: .
Main-Class: TileMolester

13
TileMolester.iml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/languages" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

BIN
TileMolester.jar Normal file

Binary file not shown.

View file

@ -0,0 +1,215 @@
################# Default English translation by Kent Hansen ##################
# File Menu
File = File
New = New...
Open = Open...
Reopen = Reopen
Close = Close
Close_All = Close All
Save = Save
Save_As = Save As...
Save_All = Save All
Exit = Exit
# View Menu
View = View
Statusbar = Statusbar
Toolbar = Toolbar
Codec = Codec
Zoom = Zoom
In = In
Out = Out
1_Dimensional = 1-Dimensional
2_Dimensional = 2-Dimensional
Block_Size = Block Size
Full_Canvas = Full Canvas
Custom_Block_Size = Custom...
Row_Interleave_Blocks = Row-interleaved
Block_Grid = Block Grid
Tile_Grid = Tile Grid
Pixel_Grid = Pixel Grid
# Edit Menu
Edit = Edit
Undo = Undo
Redo = Redo
Cant_Undo = Can't Undo
Cant_Redo = Can't Redo
Cut = Cut
Copy = Copy
Paste = Paste
Clear = Clear
Select_All = Select All
Copy_To = Copy To...
Paste_From = Paste From...
Apply_Selection = Apply Selection
New_Selection = New Selection
# Image Menu
Image = Image
Mirror = Mirror
Flip = Flip
Rotate_Right = Rotate Right
Rotate_Left = Rotate Left
Shift_Left = Shift Left
Shift_Right = Shift Right
Shift_Up = Shift Up
Shift_Down = Shift Down
Stretch = Stretch...
Canvas_Size = Canvas Size...
# Navigate Menu
Navigate = Navigate
Go_To = Go To...
Go_To_Again = Go To Again
Add_To_Bookmarks = Add To Bookmarks...
Organize_Bookmarks = Organize Bookmarks...
Bookmarks = Bookmarks
# Palette menu
Palette = Palette
Edit_Colors = Edit Colors...
Format = Format
Size = Size...
Import_From = Import From
This_File = This File...
Another_File = Another File...
Add_To_Palettes = Add To Palettes...
Organize_Palettes = Organize Palettes...
# Window Menu
Window = Window
New_Window = New Window
Tile = Tile
Cascade = Cascade
Arrange_Icons = Arrange Icons
# Help Menu
Help = Help
Help_Topics = Help Topics
Tip_of_the_Millennium = Tip of the Millennium...
About_Tile_Molester = About Tile Molester
# Tools
Selection = Selection
Dropper = Dropper
Brush = Brush
Line = Line
Flood_Fill = Flood Fill
Color_Replacer = Color Replacer
Mover = Mover
# Navigation button tooltips
Page_Back = Page Back
Page_Forward = Page Forward
Row_Back = Row Back
Row_Forward = Row Forward
Tile_Back = Tile Back
Tile_Forward = Tile Forward
Byte_Back = Byte Back
Byte_Forward = Byte Forward
# Messages
Save_Changes_To = Save changes to
Load_File_Error = Error loading file:
Save_File_Error = Error saving file:
Load_Bitmap_Error = Error loading bitmap:
Save_Bitmap_Error = Error saving bitmap:
Save_Settings_Error = Error saving settings:
Load_Settings_Error = Error loading settings:
Save_Resources_Error = Error saving resources:
Load_Resources_Error = Error loading resources:
File_Write_Error = Can't write to file:
Palette_Read_Error = Error reading palette data:
Parser_Config_Error = XML parser configuration error:
Parser_Parse_Error = Error parsing XML:
Parser_IO_Error = XML parser IO error:
Out_Of_Memory = Out of memory.
Drugs_Message = Stay off the drugs, OK?
# Built-in File Filter Names
All_Supported_Formats = All Supported Formats
All_Files = All Files (*.*)
# Bookmark/palette organization
Create_In = Create In:
New_Folder = New Folder...
Rename = Rename
Move = Move
Delete = Delete
# Confirmation button captions
Yes = Yes
No = No
OK = OK
Cancel = Cancel
# Endianness
Endianness = Byte Order
Little_Endian = Intel
Big_Endian = Motorola
# Dialog titles
New_File_Dialog_Title = Create New File
New_Folder_Dialog_Title = Create New Folder
Open_File_Dialog_Title = Open
Save_As_Dialog_Title = Save As
Copy_To_Dialog_Title = Copy To
Paste_From_Dialog_Title = Paste From
Go_To_Dialog_Title = Go To Offset
Stretch_Image_Dialog_Title = Stretch Image
Block_Size_Dialog_Title = Set Block Size
Canvas_Size_Dialog_Title = Canvas Size
Add_To_Bookmarks_Dialog_Title = Add To Bookmarks
Organize_Bookmarks_Dialog_Title = Organize Bookmarks
Add_To_Palettes_Dialog_Title = Add To Palettes
Organize_Palettes_Dialog_Title = Organize Palettes
Edit_Colors_Dialog_Title = Edit Colors
Palette_Size_Dialog_Title = Change Palette Size
New_Palette_Dialog_Title = Create New Palette
Import_Internal_Palette_Dialog_Title = Import Internal Palette
Open_Palette_Dialog_Title = Import External Palette
# Incremental Canvas Size
Decrease_Width = Decrease Width
Increase_Width = Increase Width
Decrease_Height = Decrease Height
Increase_Height = Increase Height
# Input field labels
Folder_Name_Prompt = Folder Name:
Description_Prompt = Description:
Offset_Prompt = Offset:
Size_Prompt = Size:
Columns_Prompt = Columns:
Rows_Prompt = Rows:
# Other
Empty = Empty
Offset = Offset
Mode = Mode
Radix = Radix
Hex = Hex
Dec = Dec
Absolute = Absolute
Relative = Relative

View file

@ -0,0 +1,215 @@
################# Default English translation by Kent Hansen ##################
# File Menu
File = File
New = New...
Open = Open...
Reopen = Reopen
Close = Close
Close_All = Close All
Save = Save
Save_As = Save As...
Save_All = Save All
Exit = Exit
# View Menu
View = View
Statusbar = Statusbar
Toolbar = Toolbar
Codec = Codec
Zoom = Zoom
In = In
Out = Out
1_Dimensional = 1-Dimensional
2_Dimensional = 2-Dimensional
Block_Size = Block Size
Full_Canvas = Full Canvas
Custom_Block_Size = Custom...
Row_Interleave_Blocks = Row-interleaved
Block_Grid = Block Grid
Tile_Grid = Tile Grid
Pixel_Grid = Pixel Grid
# Edit Menu
Edit = Edit
Undo = Undo
Redo = Redo
Cant_Undo = Can't Undo
Cant_Redo = Can't Redo
Cut = Cut
Copy = Copy
Paste = Paste
Clear = Clear
Select_All = Select All
Copy_To = Copy To...
Paste_From = Paste From...
Apply_Selection = Apply Selection
New_Selection = New Selection
# Image Menu
Image = Image
Mirror = Mirror
Flip = Flip
Rotate_Right = Rotate Right
Rotate_Left = Rotate Left
Shift_Left = Shift Left
Shift_Right = Shift Right
Shift_Up = Shift Up
Shift_Down = Shift Down
Stretch = Stretch...
Canvas_Size = Canvas Size...
# Navigate Menu
Navigate = Navigate
Go_To = Go To...
Go_To_Again = Go To Again
Add_To_Bookmarks = Add To Bookmarks...
Organize_Bookmarks = Organize Bookmarks...
Bookmarks = Bookmarks
# Palette menu
Palette = Palette
Edit_Colors = Edit Colors...
Format = Format
Size = Size...
Import_From = Import From
This_File = This File...
Another_File = Another File...
Add_To_Palettes = Add To Palettes...
Organize_Palettes = Organize Palettes...
# Window Menu
Window = Window
New_Window = New Window
Tile = Tile
Cascade = Cascade
Arrange_Icons = Arrange Icons
# Help Menu
Help = Help
Help_Topics = Help Topics
Tip_of_the_Millennium = Tip of the Millennium...
About_Tile_Molester = About Tile Molester
# Tools
Selection = Selection
Dropper = Dropper
Brush = Brush
Line = Line
Flood_Fill = Flood Fill
Color_Replacer = Color Replacer
Mover = Mover
# Navigation button tooltips
Page_Back = Page Back
Page_Forward = Page Forward
Row_Back = Row Back
Row_Forward = Row Forward
Tile_Back = Tile Back
Tile_Forward = Tile Forward
Byte_Back = Byte Back
Byte_Forward = Byte Forward
# Messages
Save_Changes_To = Save changes to
Load_File_Error = Error loading file:
Save_File_Error = Error saving file:
Load_Bitmap_Error = Error loading bitmap:
Save_Bitmap_Error = Error saving bitmap:
Save_Settings_Error = Error saving settings:
Load_Settings_Error = Error loading settings:
Save_Resources_Error = Error saving resources:
Load_Resources_Error = Error loading resources:
File_Write_Error = Can't write to file:
Palette_Read_Error = Error reading palette data:
Parser_Config_Error = XML parser configuration error:
Parser_Parse_Error = Error parsing XML:
Parser_IO_Error = XML parser IO error:
Out_Of_Memory = Out of memory.
Drugs_Message = Stay off the drugs, OK?
# Built-in File Filter Names
All_Supported_Formats = All Supported Formats
All_Files = All Files (*.*)
# Bookmark/palette organization
Create_In = Create In:
New_Folder = New Folder...
Rename = Rename
Move = Move
Delete = Delete
# Confirmation button captions
Yes = Yes
No = No
OK = OK
Cancel = Cancel
# Endianness
Endianness = Byte Order
Little_Endian = Intel
Big_Endian = Motorola
# Dialog titles
New_File_Dialog_Title = Create New File
New_Folder_Dialog_Title = Create New Folder
Open_File_Dialog_Title = Open
Save_As_Dialog_Title = Save As
Copy_To_Dialog_Title = Copy To
Paste_From_Dialog_Title = Paste From
Go_To_Dialog_Title = Go To Offset
Stretch_Image_Dialog_Title = Stretch Image
Block_Size_Dialog_Title = Set Block Size
Canvas_Size_Dialog_Title = Canvas Size
Add_To_Bookmarks_Dialog_Title = Add To Bookmarks
Organize_Bookmarks_Dialog_Title = Organize Bookmarks
Add_To_Palettes_Dialog_Title = Add To Palettes
Organize_Palettes_Dialog_Title = Organize Palettes
Edit_Colors_Dialog_Title = Edit Colors
Palette_Size_Dialog_Title = Change Palette Size
New_Palette_Dialog_Title = Create New Palette
Import_Internal_Palette_Dialog_Title = Import Internal Palette
Open_Palette_Dialog_Title = Import External Palette
# Incremental Canvas Size
Decrease_Width = Decrease Width
Increase_Width = Increase Width
Decrease_Height = Decrease Height
Increase_Height = Increase Height
# Input field labels
Folder_Name_Prompt = Folder Name:
Description_Prompt = Description:
Offset_Prompt = Offset:
Size_Prompt = Size:
Columns_Prompt = Columns:
Rows_Prompt = Rows:
# Other
Empty = Empty
Offset = Offset
Mode = Mode
Radix = Radix
Hex = Hex
Dec = Dec
Absolute = Absolute
Relative = Relative

View file

@ -0,0 +1,215 @@
#################### Traduzione in Italiano da Gabriele "Karvek" De Luca #####################
# File Menu
File = File
New = Nuovo...
Open = Apri...
Reopen = Riapri
Close = Chiudi
Close_All = Chiudi tutti
Save = Salva
Save_As = Salva come...
Save_All = Salva tutti
Exit = Esci
# View Menu
View = View
Statusbar = Barra di Stato
Toolbar = Barra degli Strumenti
Codec = Codifica
Zoom = Zoom
In = Dentro
Out = Fuori
1_Dimensional = 1 Dimensione
2_Dimensional = 2 Dimensioni
Block_Size = Dimensione Blocchi
Full_Canvas = Tutto il Canvas
Custom_Block_Size = Dimensione Blocchi Personalizzata
Row_Interleave_Blocks = Blocchi Rigati
Block_Grid = Griglia Blocchi
Tile_Grid = Griglia Tile
Pixel_Grid = Grigilia Pixel
# Edit Menu
Edit = Modifica
Undo = Annulla
Redo = Ripeti
Cant_Undo = Impossibile annullare
Cant_Redo = Impossibile ripetere
Cut = Taglia
Copy = Copia
Paste = Incolla
Clear = Elimina
Select_All = Seleziona tutto
Copy_To = Copia a...
Paste_From = Incolla da...
Apply_Selection = Applica selezione
New_Selection = Nuova selezione
# Image Menu
Image = Immagine
Mirror = Specchio Orizzontale
Flip = Specchio Verticale
Rotate_Right = 90° a destra
Rotate_Left = 90° a sinistra
Shift_Left = Pixel a sinistra
Shift_Right = Pixel a destra
Shift_Up = Pixel in su
Shift_Down = Pixel in giu
Stretch = Ridimensiona
Canvas_Size = Dimensione Canvas
# Navigate Menu
Navigate = Navigator
Go_To = Vai a...
Go_To_Again = Vai di nuovo a
Add_To_Bookmarks = Aggiungi a Preferiti...
Organize_Bookmarks = Organizza i Preferiti...
Bookmarks = Preferiti
# Palette menu
Palette = Palette
Edit_Colors = Modifica Colori...
Format = Formato
Size = Dimensioni
Import_From = Importa da...
This_File = Questo File...
Another_File = Un altro File..
Add_To_Palettes = Aggiungi a Palette...
Organize_Palettes = Organizza Palette...
# Window Menu
Window = Finestre
New_Window = Nuova Finestra
Tile = Tutto lo Spazio
Cascade = Di Seguito
Arrange_Icons = Ordina Icone
# Help Menu
Help = Aiuto
Help_Topics = Guida
Tip_of_the_Millennium = Suggerimento
About_Tile_Molester = About
# Tools
Selection = Selection
Dropper = Dropper
Brush = Brush
Line = Line
Flood_Fill = Flodd Fill
Color_Replacer = Color Replacer
Mover = Mover
# Navigation button tooltips
Page_Back = Pagina Indietro
Page_Forward = Pagina Avanti
Row_Back = Riga Indietro
Row_Forward = Riga Avanti
Tile_Back = Tile Indietro
Tile_Forward = Tile Avanti
Byte_Back = Byte Indietro
Byte_Forward = Byte Avanti
# Messages
Save_Changes_To = Salva modifiche a
Load_File_Error = Impossibile aprire file:
Save_File_Error = Impossibile salvare file:
Load_Bitmap_Error = Impossibile aprire bitmap:
Save_Bitmap_Error = Impossibile salvare bitmap:
Save_Settings_Error = Impossibile salvare impostazioni:
Load_Settings_Error = Impossibile caricare impostazioni:
Save_Resources_Error = Impossibile salvare risorsa:
Load_Resources_Error = Impossibile caricare risorsa:
File_Write_Error = Impossibile scrivere su file:
Palette_Read_Error = Impossibile leggere palette:
Parser_Config_Error = Errore di configurazione XML:
Parser_Parse_Error = Errore di parsing XML:
Parser_IO_Error = Errore I/O di XML:
Out_Of_Memory = Fuori limite memoria
Drugs_Message = Non utilizzare droghe, OK?
# Built-in File Filter Names
All_Supported_Formats = Tutti i file supportati
All_Files = Tutti i files (*.*)
# Bookmark/palette organization
Create_In = Crea
New_Folder = Nuova Cartella
Rename = Rinomina
Move = Sposta
Delete = Cancella
# Confirmation button captions
Yes = Si
No = No
OK = Ok
Cancel = Cancella
# Endianness
Endianness = Ordine di Byte
Little_Endian = Intel
Big_Endian = Motorola
# Dialog titles
New_File_Dialog_Title = Crea nuovo file
New_Folder_Dialog_Title = Crea nuova cartella
Open_File_Dialog_Title = Apri
Save_As_Dialog_Title = Salva come
Copy_To_Dialog_Title = Copia a
Paste_From_Dialog_Title = Incolla da
Go_To_Dialog_Title = Vai a offset
Stretch_Image_Dialog_Title = Ridimensiona Immagine
Canvas_Size_Dialog_Title = Modifica Dimensione Blocchi
Block_Size_Dialog_Title = Dimensione Canvas
Add_To_Bookmarks_Dialog_Title = Aggiungi a Preferiti
Organize_Bookmarks_Dialog_Title = Organizza Preferiti
Add_To_Palettes_Dialog_Title = Aggiungi a Palette
Organize_Palettes_Dialog_Title = Organizza Palette
Edit_Colors_Dialog_Title = Modifica Colori
Palette_Size_Dialog_Title = Cambia Dimensione Palette
New_Palette_Dialog_Title = Crea Nuova Palette
Import_Internal_Palette_Dialog_Title = Importa Palette Interna
Open_Palette_Dialog_Title = Importa Palette Esterna
# Incremental Canvas Size
Decrease_Width = Diminuisci Lunghezza
Increase_Width = Aumenta Lunghezza
Decrease_Height = Diminuisci Altezza
Increase_Height = Aumenta Altezza
# Input field labels
Folder_Name_Prompt = Nome Cartella:
Description_Prompt = Descrizione:
Offset_Prompt = Offset:
Size_Prompt = Dimensioni:
Columns_Prompt = Colonne:
Rows_Prompt = Righe:
# Other
Empty = Vuoto
Offset = Offset
Mode = Modalità
Radix = Radix
Hex = Esa
Dec = Dec
Absolute = Assoluto
Relative = Relativo

View file

@ -0,0 +1,216 @@
###################### Norsk oversettelse av Kent Hansen ######################
# Filmeny
File = Fil
New = Ny...
Open = Åpne...
Reopen = Gjenåpne
Close = Lukk
Close_All = Lukk Alle
Save = Lagre
Save_As = Lagre Som...
Save_All = Lagre Alle
Exit = Avslutt
# Redigeringsmeny
Edit = Rediger
Undo = Angre
Redo = Gjenta
Cant_Undo = Kan Ikke Angre
Cant_Redo = Kan Ikke Gjenta
Cut = Klipp Ut
Copy = Kopier
Paste = Lim Inn
Clear = Fjern
Select_All = Merk Alt
Copy_To = Kopier Til...
Paste_From = Lim Inn Fra...
Apply_Selection = Apply Selection
New_Selection = New Selection
#Vis-meny
View = Vis
Statusbar = Statuslinje
Toolbar = Verktøylinje
Codec = Kodek
Zoom = Zoom
In = Inn
Out = Ut
1_Dimensional = 1-Dimensjonell
2_Dimensional = 2-Dimensjonell
Block_Size = Blokk-størrelse
Full_Canvas = Hele Bildet
Custom_Block_Size = Egenvalgt...
Row_Interleave_Blocks = Rad-flettet
Block_Grid = Blokk-rutenett
Tile_Grid = Flis-rutenett
Pixel_Grid = Piksel-rutenett
# Bildemeny
Image = Bilde
Mirror = Vend Vannrett
Flip = Vend Loddrett
Rotate_Right = Rotér Mot Høyre
Rotate_Left = Rotér Mot Venstre
Shift_Left = Skift Til Venstre
Shift_Right = Skift Til Høyre
Shift_Up = Skift Opp
Shift_Down = Skift Ned
Stretch = Strekk...
Canvas_Size = Canvas Size...
# Navigeringsmeny
Navigate = Navigér
Go_To = Gå Til...
Go_To_Again = Gå Til Igjen
Add_To_Bookmarks = Legg Til i Bokmerker...
Organize_Bookmarks = Organisér Bokmerker...
Bookmarks = Bokmerker
# Palettmeny
Palette = Palett
Edit_Colors = Redigér Farger...
Format = Format
Size = Størrelse...
Import_From = Importér Fra
This_File = Denne Filen...
Another_File = Annen Fil...
Add_To_Palettes = Legg Til i Paletter...
Organize_Palettes = Organisér Paletter...
# Vindusmeny
Window = Vindu
New_Window = Nytt Vindu
Tile = Flislegg
Cascade = Strøm
Arrange_Icons = Ordne Ikoner
# Hjelp-meny
Help = Hjelp
Help_Topics = Emner i Hjelp...
Tip_of_the_Millennium = Millenniets Tips...
About_Tile_Molester = Om Tile Molester...
# Verktøy
Selection = Utvalg
Dropper = Hent Farge
Brush = Pensel
Line = Linje
Flood_Fill = Fyll Med Farge
Color_Replacer = Erstatt Farge
Mover = Flytt Fokus
# Navigation button tooltips
Page_Back = Side Tilbake
Page_Forward = Side Framover
Row_Back = Rad Tilbake
Row_Forward = Rad Framover
Tile_Back = Tile Tilbake
Tile_Forward = Tile Framover
Byte_Back = Byte Tilbake
Byte_Forward = Byte Framover
# Beskjeder
Save_Changes_To = Lagre endringer i
Load_File_Error = Feil ved lasting av fil:
Save_File_Error = Feil ved lagring av fil:
File_Write_Error = Kan ikke skrive til fil:
Palette_Read_Error = Feil ved lesing av palettdata:
Load_Bitmap_Error = Feil ved lasting av bildefil:
Save_Bitmap_Error = Feil ved lagring av bildefil:
Parser_Config_Error = XML parser konfigurasjonsfeil:
Parser_Parse_Error = XML parsefeil:
Parser_IO_Error = XML parser IO feil:
Save_Settings_Error = Feil ved lagring av innstillinger:
Load_Settings_Error = Feil ved lasting av innstillinger:
Save_Resources_Error = Feil ved lagring av filressurser:
Load_Resources_Error = Feil ved lasting av filressurser:
Out_Of_Memory = Tom for minne.
Drugs_Message = Hold deg unna dop, okei?
# Innebygde filfilternavn
All_Supported_Formats = Alle Støttede Filtyper
All_Files = Alle Filer (*.*)
# Bokmerke/palette-organisering
Create_In = Lagre i:
New_Folder = Ny Mappe...
Rename = Gi nytt navn
Move = Flytt
Delete = Slett
# Confirmation button captions
Yes = Ja
No = Nei
OK = OK
Cancel = Avbryt
# Endianness
Endianness = Byte Rekkefølge
Little_Endian = Intel
Big_Endian = Motorola
# Dialog titles
New_File_Dialog_Title = Opprett Ny Fil
New_Folder_Dialog_Title = Opprett Ny Mappe
Open_File_Dialog_Title = Åpne Fil
Save_As_Dialog_Title = Lagre Som
Copy_To_Dialog_Title = Kopier Til
Paste_From_Dialog_Title = Lim Inn Fra
Go_To_Dialog_Title = Gå Til Filposisjon
Stretch_Image_Dialog_Title = Strekk Bilde
Block_Size_Dialog_Title = Sett Blokk-størrelse
Canvas_Size_Dialog_Title = Canvas Size
Add_To_Bookmarks_Dialog_Title = Legg Til i Bokmerker
Organize_Bookmarks_Dialog_Title = Organisér Bokmerker
Add_To_Palettes_Dialog_Title = Legg Til i Paletter
Organize_Palettes_Dialog_Title = Organisér Paletter
Edit_Colors_Dialog_Title = Redigér Farger
Palette_Size_Dialog_Title = Endre Palettstørrelse
New_Palette_Dialog_Title = Opprett Ny Palett
Import_Internal_Palette_Dialog_Title = Importér Intern Palett
Open_Palette_Dialog_Title = Importér Ekstern Palett
# Inkrementell Canvas Size
Decrease_Width = Mink Bredde
Increase_Width = Øk Bredde
Decrease_Height = Mink Høyde
Increase_Height = Øk Høyde
# Input-felter
Folder_Name_Prompt = Mappenavn:
Description_Prompt = Description:
Offset_Prompt = Posisjon:
Size_Prompt = Størrelse:
Columns_Prompt = Kolonner:
Rows_Prompt = Rader:
# Andre
Empty = Tom
Offset = Posisjon
Mode = Modus
Radix = Tallbase
Hex = Heks.
Dec = Des.
Absolute = Absolutt
Relative = Relativ

View file

@ -0,0 +1,215 @@
################# Traducción al Español por Magimaster 2 (Billy Esquivel) ##################
# File Menu
File = Archivo
New = Nuevo...
Open = Abrir...
Reopen = Volver a Abrir
Close = Cerrar
Close_All = Cerrar Todo
Save = Salvar
Save_As = Salvar Como...
Save_All = Salvar Todo
Exit = Salir
# View Menu
View = Ver
Statusbar = Barra de Estado
Toolbar = Barra de Herramientas
Codec = Codec
Zoom = Acercamiento
In = Cerca
Out = Lejos
1_Dimensional = 1 Dimensión
2_Dimensional = 2 Dimensiones
Block_Size = Tamaño del Bloque
Full_Canvas = Cuadro Completo
Custom_Block_Size = Definir...
Row_Interleave_Blocks = Fila-Entrelazado
Block_Grid = Maya en Bloques
Tile_Grid = Maya en Mosaicos
Pixel_Grid = Maya en Pixeles
# Edit Menu
Edit = Editar
Undo = Deshacer
Redo = Rehacer
Cant_Undo = Imposible Deshacer
Cant_Redo = Imposible Rehacer
Cut = Cortar
Copy = Copiar
Paste = Pegar
Clear = Borrar
Select_All = Seleccionar Todo
Copy_To = Copiar en...
Paste_From = Pegar Desde...
Apply_Selection = Apply Selection
New_Selection = New Selection
# Image Menu
Image = Imagen
Mirror = Espejo
Flip = Voltear
Rotate_Right = Rotar a la Derecha
Rotate_Left = Rotatr a la Izquierda
Shift_Left = Desplazar a la Izquierda
Shift_Right = Desplazar a la Derecha
Shift_Up = Desplazar Hacia Arriba
Shift_Down = Desplazar Hacia Abajo
Stretch = Ajustar...
Canvas_Size = Canvas Size...
# Navigate Menu
Navigate = Navegar
Go_To = Ir A...
Go_To_Again = Ir Otra Vez
Add_To_Bookmarks = Crear Registro...
Organize_Bookmarks = Organizar Registros...
Bookmarks = Registros
# Palette menu
Palette = Paleta
Edit_Colors = Editar Colores...
Format = Formato
Size = Tamaño...
Import_From = Importar Desde
This_File = Este Archivo
Another_File = Otro Archivo...
Add_To_Palettes = Añadir a las Paletas...
Organize_Palettes = Organizar Paletas...
# Window Menu
Window = Ventana
New_Window = Nueva Ventana
Tile = Mosaico
Cascade = Cascada
Arrange_Icons = Orcanizar Íconos
# Help Menu
Help = Ayuda
Help_Topics = Temas de Ayuda
Tip_of_the_Millennium = El Consejo del Milenio...
About_Tile_Molester = Acerca de Tile Molester
# Tools
Selection = Selección
Dropper = Gotero
Brush = Pincel
Line = Línea
Flood_Fill = Rellenar
Color_Replacer = Reeplazar Color
Mover = Posición
# Navigation button tooltips
Page_Back = Página Anterior
Page_Forward = Página Siguiente
Row_Back = Fila Anteriro
Row_Forward = Fila Siguiente
Tile_Back = Mosaico Anterior
Tile_Forward = Mosaico Siguiente
Byte_Back = Byte Anterior
Byte_Forward = Byte Siguiente
# Messages
Save_Changes_To = Salvar cambios en
Load_File_Error = Error cargando archivo:
Save_File_Error = Error salvando archivo:
Load_Bitmap_Error = Error cargando mapa de bits:
Save_Bitmap_Error = Error salvando mapa de bits:
Save_Settings_Error = Error salvando preferencias:
Load_Settings_Error = Error cargando preferencias:
Save_Resources_Error = Error salvando recursos:
Load_Resources_Error = Error cargando recursos:
File_Write_Error = No se puede escribir el archivo:
Palette_Read_Error = Error leyendo los datos de la paleta:
Parser_Config_Error = Error en la configuración del analizador de XML:
Parser_Parse_Error = Error analizando XML:
Parser_IO_Error = Error IO del analizador de XML:
Out_Of_Memory = Sin memoria.
Drugs_Message = Di no a las drogas, ¿OK?
# Built-in File Filter Names
All_Supported_Formats = Todos los Formatos Compatibles
All_Files = All Files (*.*)
# Bookmark/palette organization
Create_In = Crear en:
New_Folder = Directorio Nuevo...
Rename = Cambiar Nombre
Move = Cortar
Delete = Borrar
# Confirmation button captions
Yes =
No = No
OK = OK
Cancel = Cancelar
# Endianness
Endianness = Orden de los Bytes
Little_Endian = Intel
Big_Endian = Motorola
# Dialog titles
New_File_Dialog_Title = Crear Nevo Archivo
New_Folder_Dialog_Title = Crear Nevo Carpeta
Open_File_Dialog_Title = Abrir
Save_As_Dialog_Title = Salvar Como
Copy_To_Dialog_Title = Copiar a
Paste_From_Dialog_Title = Pegar Desde
Go_To_Dialog_Title = Ir al Offset
Stretch_Image_Dialog_Title = Ajustar Imagen
Block_Size_Dialog_Title = Tamaño del Bloque
Canvas_Size_Dialog_Title = Tamaño del Cuadro
Add_To_Bookmarks_Dialog_Title = Añadir a Registros
Organize_Bookmarks_Dialog_Title = Organizar Registros
Add_To_Palettes_Dialog_Title = Añadir a Paletas
Organize_Palettes_Dialog_Title = Organizar Paletas
Edit_Colors_Dialog_Title = Ediart Colores
Palette_Size_Dialog_Title = Cabiar Tamaño de la Paleta
New_Palette_Dialog_Title = Crear Nueva Paleta
Import_Internal_Palette_Dialog_Title = Importer Paleta Interna
Open_Palette_Dialog_Title = Import Paleta Externa
# Incremental Canvas Size
Decrease_Width = Disminuir Ancho
Increase_Width = Aunmentar Ancho
Decrease_Height = Disminuir Alto
Increase_Height = Aumentar Alto
# Input field labels
Folder_Name_Prompt = Nombre del Directorio:
Description_Prompt = Descripción:
Offset_Prompt = Offset:
Size_Prompt = Tamaño:
Columns_Prompt = Columnas:
Rows_Prompt = Filas:
# Other
Empty = Vacío
Offset = Offset
Mode = Modo
Radix = Radián
Hex = Hex
Dec = Dec
Absolute = Absoluto
Relative = Relativo

213
languages/language_template Normal file
View file

@ -0,0 +1,213 @@
#################### Language template for Tile Molester #####################
# File Menu
File =
New =
Open =
Reopen =
Close =
Close_All =
Save =
Save_As =
Save_All =
Exit =
# View Menu
View =
Statusbar =
Toolbar =
Codec =
Zoom =
In =
Out =
1_Dimensional =
2_Dimensional =
Block_Size =
Full_Canvas =
Custom_Block_Size =
Row_Interleave_Blocks =
Block_Grid =
Tile_Grid =
Pixel_Grid =
# Edit Menu
Edit =
Undo =
Redo =
Cant_Undo =
Cant_Redo =
Cut =
Copy =
Paste =
Clear =
Select_All =
Copy_To =
Paste_From =
# Image Menu
Image =
Mirror =
Flip =
Rotate_Right =
Rotate_Left =
Shift_Left =
Shift_Right =
Shift_Up =
Shift_Down =
Stretch =
Canvas_Size =
# Navigate Menu
Navigate =
Go_To =
Go_To_Again =
Add_To_Bookmarks =
Organize_Bookmarks =
Bookmarks =
# Palette menu
Palette =
Edit_Colors =
Format =
Size =
Import_From =
This_File =
Another_File =
Add_To_Palettes =
Organize_Palettes =
# Window Menu
Window =
New_Window =
Tile =
Cascade =
Arrange_Icons =
# Help Menu
Help =
Help_Topics =
Tip_of_the_Millennium =
About_Tile_Molester =
# Tools
Selection =
Dropper =
Brush =
Line =
Flood_Fill =
Color_Replacer =
Mover =
# Navigation button tooltips
Page_Back =
Page_Forward =
Row_Back =
Row_Forward =
Tile_Back =
Tile_Forward =
Byte_Back =
Byte_Forward =
# Messages
Save_Changes_To =
Load_File_Error =
Save_File_Error =
Load_Bitmap_Error =
Save_Bitmap_Error =
Save_Settings_Error =
Load_Settings_Error =
Save_Resources_Error =
Load_Resources_Error =
File_Write_Error =
Palette_Read_Error =
Parser_Config_Error =
Parser_Parse_Error =
Parser_IO_Error =
Out_Of_Memory =
Drugs_Message =
# Built-in File Filter Names
All_Supported_Formats =
All_Files =
# Bookmark/palette organization
Create_In =
New_Folder =
Rename =
Move =
Delete =
# Confirmation button captions
Yes =
No =
OK =
Cancel =
# Endianness
Endianness =
Little_Endian =
Big_Endian =
# Dialog titles
New_File_Dialog_Title =
New_Folder_Dialog_Title =
Open_File_Dialog_Title =
Save_As_Dialog_Title =
Copy_To_Dialog_Title =
Paste_From_Dialog_Title =
Go_To_Dialog_Title =
Stretch_Image_Dialog_Title =
Canvas_Size_Dialog_Title =
Block_Size_Dialog_Title =
Add_To_Bookmarks_Dialog_Title =
Organize_Bookmarks_Dialog_Title =
Add_To_Palettes_Dialog_Title =
Organize_Palettes_Dialog_Title =
Edit_Colors_Dialog_Title =
Palette_Size_Dialog_Title =
New_Palette_Dialog_Title =
Import_Internal_Palette_Dialog_Title =
Open_Palette_Dialog_Title =
# Incremental Canvas Size
Decrease_Width =
Increase_Width =
Decrease_Height =
Increase_Height =
# Input field labels
Folder_Name_Prompt =
Description_Prompt =
Offset_Prompt =
Size_Prompt =
Columns_Prompt =
Rows_Prompt =
# Other
Empty =
Offset =
Mode =
Radix =
Hex =
Dec =
Absolute =
Relative =

38
resources/tmres.dtd Normal file
View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!ELEMENT tmres (bookmarks?, palettes?)>
<!ELEMENT bookmarks (folder | bookmark)*>
<!ELEMENT palettes (folder | palette)*>
<!ELEMENT folder (name, (folder | bookmark | palette)*)>
<!ELEMENT bookmark (description)>
<!ATTLIST bookmark
offset CDATA #REQUIRED
columns CDATA #REQUIRED
rows CDATA #REQUIRED
blockwidth CDATA #REQUIRED
blockheight CDATA #REQUIRED
rowinterleaved (true | false) #REQUIRED
sizeblocktocanvas (true | false) #REQUIRED
mode (1D | 2D) #REQUIRED
codec CDATA #REQUIRED
palIndex CDATA #REQUIRED
palette CDATA #IMPLIED
>
<!ELEMENT palette (description, data?)>
<!ATTLIST palette
id ID #IMPLIED
direct (yes | no) #REQUIRED
codec CDATA #REQUIRED
size CDATA #REQUIRED
offset CDATA #IMPLIED
endianness (little | big) "little"
>
<!ELEMENT data (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT description (#PCDATA)>

9
settings.dtd Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!ELEMENT settings (property*)>
<!ELEMENT property EMPTY>
<!ATTLIST property
key CDATA #REQUIRED
value CDATA #REQUIRED
>

9
settings.xml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE settings SYSTEM "settings.dtd">
<settings>
<property key="locale" value="en_US"/>
<property key="viewStatusBar" value="true"/>
<property key="viewToolBar" value="true"/>
<property key="maxRecentFiles" value="10"/>
<property key="lastPath" value=""/>
</settings>

52
src/TileMolester.java Normal file
View file

@ -0,0 +1,52 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
import tm.ui.TMUI;
/**
*
* Tile Molester main class.
* A quite pointless class really. The application is very UI-centric,
* so the TMUI class evolved into the real application backbone.
* This class just gets the show started.
*
**/
public class TileMolester {
/**
*
* Constructor.
*
**/
public TileMolester() {
new TMUI();
}
/**
*
* Starts up the program.
*
**/
public static void main(String[] args) {
new TileMolester();
}
}

221
src/tm/FileImage.java Normal file
View file

@ -0,0 +1,221 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm;
import tm.treenodes.*;
import tm.ui.TMView;
import java.io.File;
import java.util.Vector;
/**
*
* A FileImage object represents a file that has been loaded into the editor.
*
**/
public class FileImage {
private byte[] contents;
private File file;
private Vector views = new Vector();
private static int fileNum=0;
private boolean modified;
private TMFileResources resources;
/**
*
* Creates a FileImage from a file on disk.
*
* The contents of the file have already been read into buffer contents.
*
**/
public FileImage(File file, byte[] contents) {
this.file = file;
this.contents = contents;
this.resources = null;
setModified(false);
}
/**
*
* Creates a blank FileImage of the requested size.
*
**/
public FileImage(int size) throws OutOfMemoryError {
file = new File(System.getProperty("user.dir") + (fileNum++));
this.resources = null;
try {
contents = new byte[size];
}
catch (OutOfMemoryError e) {
throw e;
}
// fill with zeroes
for (int i=0; i<size; i++) {
contents[i] = 0x00;
}
setModified(false);
}
/**
*
* Sets the file.
*
**/
public void setFile(File file) {
this.file = file;
}
/**
*
* Gets the file.
*
**/
public File getFile() {
return file;
}
/**
*
* Gets the binary contents of this FileImage.
*
**/
public byte[] getContents() {
return contents;
}
/**
*
* Gets the size (in # of bytes) of the file contents.
*
**/
public int getSize() {
return contents.length;
}
/**
*
* Gets the name of the file.
*
**/
public String getName() {
return file.getName();
}
/**
*
* Gets the TMViews associated with this FileImage.
*
**/
public TMView[] getViews() {
TMView[] vs = new TMView[views.size()];
for (int i=0; i<vs.length; i++) {
vs[i] = (TMView)views.get(i);
}
return vs;
}
/**
*
* Adds a TMView to this FileImage.
*
**/
public void addView(TMView view) {
views.add(view);
}
/**
*
* Removes a TMView for this FileImage.
*
**/
public void removeView(TMView view) {
views.remove(view);
if (views.size() == 0) {
contents = null; // kill the contents reference
resources = null;
file = null;
}
}
/**
*
* Sets the modified flag.
*
**/
public void setModified(boolean modified) {
this.modified = modified;
// update view titles
TMView[] views = getViews();
for (int i=0; i<views.length; i++) {
TMView v = views[i];
String t = v.getTitle();
if (modified) {
v.setTitle(getName()+"*");
v.getTMUI().setSaveButtonsEnabled(true);
}
else {
v.setTitle(getName());
}
}
}
/**
*
* Gets the modified flag.
*
**/
public boolean isModified() {
return modified;
}
/**
*
* Sets the resources associated with this fileimage.
* The resources contain bookmarks and palettes.
*
**/
public void setResources(TMFileResources resources) {
this.resources = resources;
}
/**
*
* Gets the resources related to this fileimage.
*
**/
public TMFileResources getResources() {
return resources;
}
}

View file

@ -0,0 +1,135 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm;
import tm.gfxlibs.*;
import tm.canvases.TMTileCanvas;
import tm.osbaldeston.image.BMP;
import javax.imageio.*;
import javax.imageio.stream.FileImageOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.awt.Image;
import java.awt.image.RenderedImage;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
/**
*
* Allows a tile canvas to be saved to a bitmap.
*
**/
public class TMBitmapExporter {
/**
*
* Static method that takes a tile canvas and a file and saves the tiles as a
* bitmap. The bitmap format is determined by the extension of the file.
*
**/
public static void saveTileCanvasToFile(TMTileCanvas canvas, File file)
throws Exception {
Image img = canvas.getImage();
String ext = getExtension(file);
// use proper encoder based on file extension
if (ext.equals("bmp")) {
BMP bmp = new BMP(img);
bmp.write(file);
}
else if (ext.equals("gif")) {
try {
FileOutputStream fis = new FileOutputStream(file);
GIFOutputStream.writeGIF(fis, img, GIFOutputStream.STANDARD_256_COLORS);
fis.close();
}
catch (Exception e) {
throw e;
}
}
else if (ext.equals("jpg")) {
ImageWriter jpegEncoder = (ImageWriter)ImageIO.getImageWritersByFormatName("jpeg").next();
if (jpegEncoder != null) {
try {
FileImageOutputStream fios = new FileImageOutputStream(file);
jpegEncoder.setOutput(fios);
jpegEncoder.write(convertToRenderedImage(img));
fios.close();
}
catch (Exception e) {
throw e;
}
}
}
else if (ext.equals("png")) {
try {
byte[] pngBytes = new PngEncoder(img).pngEncode();
FileOutputStream fis = new FileOutputStream(file);
fis.write(pngBytes);
fis.close();
}
catch (Exception e) {
throw e;
}
}
else if (ext.equals("pcx")) {
try {
byte[] pcxBytes = PCXEncoder.encode(img);
FileOutputStream fis = new FileOutputStream(file);
fis.write(pcxBytes);
fis.close();
}
catch (Exception e) {
throw e;
}
}
}
/**
*
* Gets file extension.
*
**/
public static String getExtension(File f) {
String ext = "";
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i+1).toLowerCase();
}
return ext;
}
/**
*
* Converts an Image to a RenderedImage so that it can be fed to the JPEG encoder.
*
**/
public static RenderedImage convertToRenderedImage(Image img) {
BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
g2d.drawImage(img, 0,0,img.getWidth(null), img.getHeight(null), null);
return bi;
}
}

View file

@ -0,0 +1,130 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm;
import tm.tilecodecs.*;
import tm.canvases.TMTileCanvas;
import tm.osbaldeston.image.BMP;
import tm.gfxlibs.*;
import java.io.File;
import java.io.FileInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
/**
*
* Allows a bitmap to be loaded and converted to a tile canvas.
*
**/
public class TMBitmapImporter {
/**
*
* Static method that takes a file, tries to load the file as a bitmap and
* convert it to a tile canvas. The file extension is used to determine what
* bitmap format to use when loading the file.
*
**/
public static TMTileCanvas loadTileCanvasFromFile(File file)
throws Exception {
Image img = null;
String ext = getExtension(file);
// use proper decoder based on file extension
if (ext.equals("bmp")) {
// use BMP decoder
BMP bmp = new BMP(file);
img = bmp.getImage();
}
else if (ext.equals("pcx")) {
// use PCX decoder
try {
img = PCXReader.loadImage(new FileInputStream(file));
}
catch (Exception e) {
throw e;
}
}
else {
// use Java's decoders
ImageIcon icon = new ImageIcon(file.getAbsolutePath());
img = icon.getImage();
}
if (img == null) {
throw new Exception(); // couldn't load the image
}
int w = img.getWidth(null);
int h = img.getHeight(null);
int[] pixels = new int[w * h];
// grab the pixels
PixelGrabber pg = new PixelGrabber(img, 0, 0, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
throw e;
}
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
throw new Exception();
}
DirectColorTileCodec codec = new DirectColorTileCodec("", 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, "");
// copy to a new canvas
int cols = w / 8;
int rows = h / 8;
byte[] bits = new byte[cols * rows * codec.getTileSize()];
TMTileCanvas tc = new TMTileCanvas(bits);
tc.setGridSize(cols, rows);
tc.setPalette(null);
tc.setMode(TileCodec.MODE_1D);
tc.setCodec(codec);
int w2 = tc.getCanvasWidth();
int h2 = tc.getCanvasHeight();
for (int i=0; i<h2; i++) {
for (int j=0; j<w2; j++) {
tc.setPixel(j, i, pixels[(i * w) + j]);
}
}
tc.packPixels();
return tc;
}
/**
*
* Gets file extension.
*
**/
public static String getExtension(File f) {
String ext = "";
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i+1).toLowerCase();
}
return ext;
}
}

358
src/tm/TMFileResources.java Normal file
View file

@ -0,0 +1,358 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm;
import tm.colorcodecs.*;
import tm.tilecodecs.*;
import tm.treenodes.*;
import tm.utils.*;
import tm.ui.TMUI;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
/**
*
* Holds resources for a file image.
* Resources include bookmarks and palettes.
*
**/
public class TMFileResources {
private FolderNode bookmarkRoot;
private FolderNode paletteRoot;
private FileImage fileImage;
private TMUI ui;
/**
*
* Create initial resources for the specified fileimage.
*
**/
public TMFileResources(FileImage fileImage, TMUI ui) {
this.bookmarkRoot = new FolderNode(ui.xlate("Bookmarks"));
this.paletteRoot = new FolderNode(ui.xlate("Palettes"));
fileImage.setResources(this);
}
/**
*
* Loads resources for a fileimage from an XML document.
*
* Resources are in an XML tree structure like so:
* +resources
* +resourcetype1
* +resource1
* +resource2
* +resourcetype2
* ....
* +resourcetype3
*
**/
public TMFileResources(File file, FileImage fileImage, TMUI ui)
throws SAXException, ParserConfigurationException, IOException {
Document doc = null;
try {
doc = XMLParser.parse(file);
} catch (SAXException e) {
throw e;
}
catch (ParserConfigurationException e) {
throw e;
}
catch (IOException e) {
throw e;
}
if (doc == null) return;
this.fileImage = fileImage;
this.ui = ui;
Element root = doc.getDocumentElement();
bookmarkRoot = parseBookmarks(root);
paletteRoot = parsePalettes(root);
fileImage.setResources(this);
}
/**
*
* Parses the bookmarks into a tree of TMTreeNodes.
* Bookmarks are in an XML tree structure like so:
* +folder1
* +bookmark1
* +bookmark2
* +folder2
* +folder3
* +bookmark1
* ...
*
**/
public FolderNode parseBookmarks(Element root) {
Element e = getChildTag(root, "bookmarks", 0);
FolderNode bookmarkRoot = new FolderNode(ui.xlate("Bookmarks"));
if (e != null) {
// parse bookmarks into tree
NodeList children = e.getChildNodes();
for (int i=0; i<children.getLength(); i++) {
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
addToBookmarksTree((Element)children.item(i), bookmarkRoot);
}
}
}
return bookmarkRoot;
}
/**
*
* Recursively adds folders and bookmarks to tree.
*
**/
public void addToBookmarksTree(Element e, FolderNode folder) {
if (e.getTagName().equals("folder")) {
// subfolder
String name = XMLParser.getNodeValue(getChildTag(e, "name", 0));
FolderNode subFolder = new FolderNode(name);
folder.add(subFolder);
NodeList children = e.getChildNodes();
for (int i=0; i<children.getLength(); i++) {
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
addToBookmarksTree((Element)children.item(i), subFolder);
}
}
}
else if (e.getTagName().equals("bookmark")) {
// read a bookmark
int offset = Integer.parseInt(e.getAttribute("offset"));
int cols = Integer.parseInt(e.getAttribute("columns"));
int rows = Integer.parseInt(e.getAttribute("rows"));
int blockWidth = Integer.parseInt(e.getAttribute("blockwidth"));
int blockHeight = Integer.parseInt(e.getAttribute("blockheight"));
boolean rowInterleaved = e.getAttribute("rowinterleaved").equals("true");
boolean sizeBlockToCanvas = e.getAttribute("sizeblocktocanvas").equals("true");
int mode = TileCodec.MODE_1D;
if (e.getAttribute("mode").equals("2D")) {
mode = TileCodec.MODE_2D;
}
int palIndex = Integer.parseInt(e.getAttribute("palIndex"));
// String palID = e.getAttribute("palette");
String codecID = e.getAttribute("codec");
TileCodec codec = ui.getTileCodecByID(codecID);
String desc = XMLParser.getNodeValue(getChildTag(e, "description", 0));
BookmarkItemNode bookmark =
new BookmarkItemNode(
offset,
cols,
rows,
blockWidth,
blockHeight,
rowInterleaved,
sizeBlockToCanvas,
mode,
palIndex,
codec,
desc
);
folder.add(bookmark);
}
}
/**
*
* Parses the palettes into a tree of TMTreeNodes.
* Highly analoguous to bookmark parsing.
*
**/
public FolderNode parsePalettes(Element root) {
Element e = getChildTag(root, "palettes", 0);
FolderNode paletteRoot = new FolderNode(ui.xlate("Palettes"));
if (e != null) {
// parse palettes into tree
NodeList children = e.getChildNodes();
for (int i=0; i<children.getLength(); i++) {
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
addToPalettesTree((Element)children.item(i), paletteRoot);
}
}
}
return paletteRoot;
}
/**
*
* Recursively processes and adds folders and palettes to tree.
*
**/
public void addToPalettesTree(Element e, FolderNode folder) {
if (e.getTagName().equals("folder")) {
// subfolder
String name = XMLParser.getNodeValue(getChildTag(e, "name", 0));
FolderNode subFolder = new FolderNode(name);
folder.add(subFolder);
NodeList children = e.getChildNodes();
for (int i=0; i<children.getLength(); i++) {
if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
addToPalettesTree((Element)children.item(i), subFolder);
}
}
}
else if (e.getTagName().equals("palette")) {
// read palette
String id = e.getAttribute("id");
boolean direct = e.getAttribute("direct").equals("yes");
String codecID = e.getAttribute("codec");
ColorCodec codec = ui.getColorCodecByID(codecID);
int size = Integer.parseInt(e.getAttribute("size"));
int offset = 0;
int endianness = ColorCodec.LITTLE_ENDIAN;
if (e.getAttribute("endianness").equals("big")) {
endianness = ColorCodec.BIG_ENDIAN;
}
byte[] data;
if (direct) {
// data is in XML, parse it
String hexString = XMLParser.getNodeValue(getChildTag(e, "data", 0));
// alt: hexString = ((CDATASection)palette.getFirstChild()).getData());
data = HexStringConverter.hexStringToBytes(hexString);
}
else {
// data is in file
offset = Integer.parseInt(e.getAttribute("offset"));
data = fileImage.getContents();
}
String desc = XMLParser.getNodeValue(getChildTag(e, "description", 0));
TMPalette pal = new TMPalette(id, data, offset, size, codec, endianness, direct, false);
PaletteItemNode palette =
new PaletteItemNode(
pal,
desc
);
folder.add(palette);
}
}
/**
*
* Gets the root of the tree of bookmarks & bookmark folders.
*
**/
public FolderNode getBookmarksRoot() {
return bookmarkRoot;
}
/**
*
* Gets the root of the tree of palettes & palette folders.
*
**/
public FolderNode getPalettesRoot() {
return paletteRoot;
}
/**
*
* Returns XML representation of resources.
*
**/
public String toXML() {
StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
sb.append("<!DOCTYPE tmres SYSTEM \"tmres.dtd\">\n");
sb.append("<tmres>\n");
sb.append(bookmarksToXML());
sb.append(palettesToXML());
sb.append("</tmres>\n");
return sb.toString();
}
/**
*
* Return XML representation of bookmarks.
*
**/
public String bookmarksToXML() {
StringBuffer sb = new StringBuffer();
sb.append(" <bookmarks>\n");
TMTreeNode[] children = bookmarkRoot.getChildren();
for (int i=0; i<children.length; i++) {
sb.append(children[i].toXML());
}
sb.append(" </bookmarks>\n");
return sb.toString();
}
/**
*
* Returns XML representation of palettes.
*
**/
public String palettesToXML() {
StringBuffer sb = new StringBuffer();
sb.append(" <palettes>\n");
TMTreeNode[] children = paletteRoot.getChildren();
for (int i=0; i<children.length; i++) {
sb.append(children[i].toXML());
}
sb.append(" </palettes>\n");
return sb.toString();
}
/**
*
* Gets the default resource filename for the given file.
* Currently, this is [filename-extension+".xml"]
*
**/
public static File getResourceFileFor(File file) {
// determine name of XML resource file based on filename
String name = file.getName();
int i = name.lastIndexOf('.');
if (i != -1) {
name = name.substring(0, i);
}
name += ".xml";
return new File("./resources/"+name);
}
private Element getChildTag(Element e, String Tag, int i) {
return (Element)e.getElementsByTagName(Tag).item(i);
}
}

457
src/tm/TMPalette.java Normal file
View file

@ -0,0 +1,457 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm;
import tm.colorcodecs.*;
/**
*
* Custom palette class.
*
**/
public class TMPalette {
// the default 256-color palette
public static final int[] defaultPalette = {
0x000000, 0xA04020, 0xD0A050, 0xF0F080, 0x860033, 0x0073BF, 0xFFCF00, 0xB4EBEF,
0x000093, 0x00FF51, 0x00ACFF, 0xA411BC, 0x000000, 0xF28C59, 0x9F00B6, 0x00DC83,
0x090021, 0xFFFFFF, 0xFF9F00, 0xFF0003, 0xFFAFC2, 0xF0004B, 0xE136FC, 0x6D0473,
0x000300, 0xFFF94B, 0x9FA300, 0x636000, 0x000000, 0xF05CF9, 0xEF6B84, 0x00FF5D,
0x000000, 0x99E200, 0xFF2F4C, 0x5EFFD6, 0x002D24, 0xFFFF00, 0x4AC234, 0x4838F5,
0x4300D6, 0xFF0000, 0xE8890B, 0x00FC84, 0x004066, 0xFFD200, 0xBBD96B, 0xFF0067,
0xFF6900, 0x330059, 0xFFFF4B, 0x1BACFB, 0x5EAAE3, 0x4F5DF9, 0xB12A8E, 0x6DF674,
0xEB07E3, 0x9EB76E, 0xEE5FD6, 0x2BF8BA, 0x723338, 0x5D063C, 0xDC9C6F, 0xAA2B12,
0x176599, 0xC6671B, 0x61EC0D, 0x820194, 0x978F12, 0x77EAB5, 0x4A9936, 0x099FAF,
0x956558, 0x0FD972, 0xF24CA5, 0x47F7A2, 0x067996, 0xA1ABB4, 0x633056, 0xE02D81,
0x2A5836, 0x9DCAE0, 0x65C043, 0xC4B9D5, 0x11A983, 0xF6DB7D, 0x8742B1, 0xB17615,
0x218E7A, 0xF1577E, 0x943F95, 0xEADDED, 0xC0D74E, 0xA84090, 0xA79252, 0xF8EE81,
0xBCCD29, 0x0EA376, 0xE5A101, 0x284457, 0xB5512D, 0x2FC653, 0xEBA13F, 0x2DFDA6,
0x479061, 0x41BBDC, 0xC53597, 0x5F4CBE, 0xBF6749, 0x78AE54, 0xB5BD0C, 0x7641F9,
0x69B4F7, 0xD7049F, 0x7A487F, 0x52AD20, 0x6E7F52, 0x5A9633, 0x607DDB, 0x02355C,
0xF07278, 0xE8DE01, 0xA19835, 0xA0B90E, 0x98EE91, 0xBC46BD, 0xBD67CE, 0xE87CA2,
0x7B6CFB, 0x0ACF97, 0xD3682E, 0x5CFB6A, 0x243CA9, 0xE943F2, 0x1D736F, 0x26F742,
0x7A8E1A, 0xE708FD, 0x4006EB, 0xA2AEF0, 0xF085CB, 0x22DDC2, 0xEB03CB, 0x879218,
0x5E0F42, 0x3E2F83, 0x73880A, 0x56707B, 0x9526A4, 0xA23E37, 0x2959BE, 0xB115E2,
0x29F61D, 0xCA4BC2, 0x234876, 0x2DC7E5, 0xC735C2, 0xF41484, 0x6B7D1D, 0x1C4FAD,
0x79AAC3, 0xA90949, 0xF4CD7B, 0x7BD0AE, 0x25EA81, 0x2B5E23, 0xB50961, 0x53DC47,
0xF58C41, 0x41DCE8, 0x423AA4, 0xDE3864, 0x2A45A6, 0x011F57, 0x75A7B4, 0x621B26,
0xDE88C0, 0xC05A68, 0xE9AE99, 0x81E7B6, 0x514F2C, 0x9E72A6, 0x5EF1C2, 0xF7175A,
0xFD219A, 0x8EF152, 0x2171FC, 0x502258, 0x88C63A, 0x1945F8, 0x3CAA21, 0xD7A067,
0x52CCF2, 0x4CE400, 0x43CC98, 0x1EDCF4, 0x076F0E, 0xB79188, 0xAF0F2B, 0x0AD644,
0xD85343, 0xB6ECA3, 0x0662BB, 0x5A816A, 0x26A60E, 0xC21E72, 0xDFC212, 0x286621,
0xF6FAD6, 0x2BA27D, 0x5F6F12, 0x306F18, 0xF80C80, 0x1066A7, 0xAC784C, 0x9275FA,
0x28C6D3, 0x2C85D8, 0x8EF002, 0xE12571, 0x7A8AFB, 0x2B6B5E, 0x73862A, 0x44E76F,
0x9DCDF8, 0x375493, 0xEA65D1, 0xD745F0, 0x3352E7, 0xD648A3, 0xDC30AD, 0x920FD5,
0xE9572A, 0x35A93C, 0x9BFCA9, 0x37F35D, 0x143E89, 0x7FA505, 0xD85EFA, 0x63DF30,
0xC8CCB8, 0xDD0EA2, 0x1F28D4, 0xDF6DB3, 0xEDD7A7, 0x909954, 0x88BE41, 0x9DBCA7,
0x1B3801, 0x91A0A0, 0xCFD4F6, 0x76CDD7, 0x6D318A, 0x22D03B, 0x7750D3, 0x553080
};
private static int palNum=0;
private String id; // unique ID for this palette
private int[] entries; // stored in native format
private int[] rgbvalues; // corresponding RGB values
private ColorCodec codec; // used to convert from/to native/RGB format
private int endianness;
private boolean direct; // Mewster: I SUPPOSE it means a palette has been loaded from another file
private int offset;
private boolean modified;
/**
*
* Creates a new palette with given format and size.
*
**/
public TMPalette(String id, int size, ColorCodec codec, int endianness) {
this.id = id;
this.codec = codec;
this.endianness = endianness;
this.direct = true;
this.offset = 0;
entries = new int[size];
rgbvalues = new int[size];
// set all entries to zero
for (int i=0; i<size; i++) {
setEntry(i, 0);
}
setModified(false);
}
/**
*
* Creates a new palette with given format and entries.
*
**/
public TMPalette(String id, int[] entries, ColorCodec codec, int endianness, boolean direct) {
this.id = id;
this.codec = codec;
this.endianness = endianness;
this.direct = direct;
this.offset = 0;
this.entries = new int[entries.length];
rgbvalues = new int[entries.length];
for (int i=0; i<entries.length; i++) {
setEntry(i, entries[i]);
}
setModified(false);
}
/**
*
* Creates a new palette with given format and entries.
*
**/
public TMPalette(String id, byte[] bytes, int offset, int size, ColorCodec codec, int endianness, boolean direct, boolean modified) {
this.id = id;
this.codec = codec;
this.endianness = endianness;
this.offset = offset;
this.direct = direct;
this.entries = new int[size];
rgbvalues = new int[size];
codec.setEndianness(endianness);
for (int i=0; i<size; i++) {
setEntry(i, codec.fromBytes(bytes, offset + (i * codec.getBytesPerPixel())));
}
setModified(modified);
}
/**
*
* Creates a new palette from an existing palette.
* The settings and entries are copied from the given palette.
* TODO: How to create unique ID??
*
**/
public TMPalette(TMPalette palette) {
id = "PAL" + (palNum++); // TODO
codec = palette.getCodec();
endianness = palette.getEndianness();
offset = palette.getOffset();
direct = palette.isDirect();
entries = new int[palette.getSize()];
rgbvalues = new int[palette.getSize()];
for (int i=0; i<palette.getSize(); i++) {
entries[i] = palette.getEntry(i);
rgbvalues[i] = palette.getEntryRGB(i);
}
setModified(false);
}
/**
*
* Gets the file offset where this palette was loaded from (0 if it wasn't
* loaded from a file.)
*
**/
public int getOffset() {
return offset;
}
/**
*
* Returns whether this palette is "direct".
*
**/
public boolean isDirect() {
return direct;
}
/**
*
* Sets the endianness.
* This is needed when converting the array of entries from/to an
* array of bytes.
*
**/
public void setEndianness(int endianness) {
this.endianness = endianness;
setModified(true);
}
/**
*
* Gets the endianness.
*
**/
public int getEndianness() {
return endianness;
}
/**
*
* Gets the ID for this palette.
*
**/
public String getID() {
return id;
}
/**
*
* Sets a palette entry.
*
**/
public void setEntry(int index, int value) {
entries[index] = value;
rgbvalues[index] = codec.decode(value);
setModified(true);
}
/**
*
* Sets a palette entry by RGB value.
*
**/
public void setEntryRGB(int index, int rgb) {
entries[index] = codec.encode(rgb);
rgbvalues[index] = codec.decode(entries[index]);
setModified(true);
}
/**
*
* Gets a palette entry.
*
**/
public int getEntry(int index) {
if (index >= entries.length) {
System.out.println("Entry doesn't exist");
return 0;
}
return entries[index];
}
/**
*
* Gets a palette entry's 24-bit RGB value.
*
**/
public int getEntryRGB(int index) throws ArrayIndexOutOfBoundsException {
int val = 0;
try {
val = rgbvalues[index];
} catch (ArrayIndexOutOfBoundsException e) {
val = 0;
}
return val;
}
/**
*
* Gets the palette entry as a byte array.
*
**/
public byte[] getEntryBytes(int index) throws ArrayIndexOutOfBoundsException {
byte[] bytes = new byte[codec.getBytesPerPixel()];
codec.setEndianness(endianness);
try {
codec.toBytes(entries[index], bytes, 0);
} catch (ArrayIndexOutOfBoundsException e) {
return bytes;
}
return bytes;
}
/**
*
* Gets index of rgb value _relative_ to startIndex.
*
**/
public int indexOf(int startIndex, int rgbval) {
for (int i=0; i<entries.length-startIndex; i++) {
if (getEntryRGB(startIndex+i) == rgbval) {
return i;
}
}
System.out.println("RGB value not in palette");
return -1;
}
/**
*
* Gets the array of palette entries.
*
**/
public int[] getEntries() {
return entries;
}
/**
*
* Gets the size of the palette.
*
**/
public int getSize() {
return entries.length;
}
/**
*
* Resizes the palette.
*
**/
public void setSize(int size) {
int[] tempEntries = new int[size];
int[] tempRgbvalues = new int[size];
if (size > entries.length) {
// copy all old entries
for (int i=0; i<entries.length; i++) {
tempEntries[i] = entries[i];
tempRgbvalues[i] = rgbvalues[i];
}
// clear remaining entries
for (int i=entries.length; i<size; i++) {
tempEntries[i] = codec.encode(0x000000);
tempRgbvalues[i] = 0x000000;
}
}
else {
// copy as many old entries as possible
for (int i=0; i<size; i++) {
tempEntries[i] = entries[i];
tempRgbvalues[i] = rgbvalues[i];
}
}
entries = tempEntries;
rgbvalues = tempRgbvalues;
setModified(true);
}
/**
*
* Sets the color codec. Converts entries to the new format.
*
**/
public void setCodec(ColorCodec codec) {
this.codec = codec;
// do conversion
for (int i=0; i<entries.length; i++) {
setEntry(i, codec.encode(rgbvalues[i]));
}
setModified(true);
}
/**
*
* Gets the color codec for this palette.
*
**/
public ColorCodec getCodec() {
return codec;
}
/**
*
* Returns the palette entry # whose RGB components match the given value the closest.
* startIndex..colorCount is the range of colors that are tried.
* PS: The returned index is RELATIVE to startIndex.
*
**/
public int closestMatchingEntry(int startIndex, int colorCount, int argb) {
int targetR = (argb & 0x00FF0000) >> 16;
int targetG = (argb & 0x0000FF00) >> 8;
int targetB = (argb & 0x000000FF);
int bestEntry=0, bestDiff=1000000;
for (int i=0; i<colorCount; i++) {
int val = getEntryRGB(startIndex + i);
int r = (val & 0x00FF0000) >> 16;
int g = (val & 0x0000FF00) >> 8;
int b = (val & 0x000000FF);
int diff = Math.abs(targetR - r) + Math.abs(targetG - g) + Math.abs(targetB - b);
if (diff < bestDiff) {
bestDiff = diff;
bestEntry = i;
}
}
return bestEntry;
}
/**
*
* Convenience method for getting the actual RGB value of the closest matching
* entry, instead of the palette index.
*
**/
public int closestMatchingEntryRGB(int startIndex, int colorCount, int argb) {
return getEntryRGB(startIndex + closestMatchingEntry(startIndex, colorCount, argb));
}
/**
*
* Returns the palette entries as a byte array.
*
**/
public byte[] entriesToBytes() {
byte[] bytes = new byte[entries.length * codec.getBytesPerPixel()];
codec.setEndianness(endianness);
for (int i=0; i<entries.length; i++) {
codec.toBytes(entries[i], bytes, i * codec.getBytesPerPixel());
}
return bytes;
}
/**
*
* Sets the modified state of this palette.
*
**/
public void setModified(boolean modified) {
this.modified = modified;
}
/**
*
* Gets the modified state of this palette.
*
**/
public boolean isModified() {
return modified;
}
}

371
src/tm/TMSpecReader.java Normal file
View file

@ -0,0 +1,371 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm;
import tm.colorcodecs.*;
import tm.tilecodecs.*;
import tm.fileselection.*;
import tm.utils.*;
import java.util.StringTokenizer;
import java.util.Vector;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
/**
*
* This class has methods for reading an XML document containing specifications
* for the various program resources.
*
**/
public class TMSpecReader {
private static Vector colorcodecs;
private static Vector tilecodecs;
private static Vector filefilters;
private static Vector palettefilters;
private static Vector filelisteners;
private static Element tmspec;
/**
*
* Reads specs from the specified file.
*
**/
public static void readSpecsFromFile(File file)
throws SAXException, ParserConfigurationException, IOException {
colorcodecs = new Vector();
tilecodecs = new Vector();
filefilters = new Vector();
palettefilters = new Vector();
filelisteners = new Vector();
Document doc = null;
try {
doc = XMLParser.parse(file);
} catch (SAXException e) {
throw e;
}
catch (ParserConfigurationException e) {
throw e;
}
catch (IOException e) {
throw e;
}
if (doc == null) return;
tmspec = doc.getDocumentElement(); // root element (<tmspec> tag)
readDirectColorFormats();
readIndexedColorFormats();
readPlanarTileFormats();
readLinearTileFormats();
readDirectColorTileFormats();
readCompositeTileFormats();
readFileFilters();
readPaletteFilters();
readFileListeners();
}
/**
*
* Reads the directcolor format tags and creates directcolorcodecs for them.
*
**/
private static void readDirectColorFormats() {
NodeList directColorTags = tmspec.getElementsByTagName("directcolor");
for (int i=0; i<directColorTags.getLength(); i++) {
Element dc = (Element)directColorTags.item(i);
String id = dc.getAttribute("id");
int bpp = Integer.parseInt(dc.getAttribute("bpp"));
int rmask = (int)Long.parseLong(dc.getAttribute("rmask"), 16);
int gmask = (int)Long.parseLong(dc.getAttribute("gmask"), 16);
int bmask = (int)Long.parseLong(dc.getAttribute("bmask"), 16);
int amask = 0;
if (!dc.getAttribute("amask").equals(""))
amask = (int)Long.parseLong(dc.getAttribute("amask"), 16);
String desc = XMLParser.getNodeValue(dc.getElementsByTagName("description").item(0));
colorcodecs.add(new DirectColorCodec(id, bpp, rmask, gmask, bmask, amask, desc));
}
}
/**
*
* Reads the indexedcolor format tags and creates indexedcolorcodecs for them.
*
**/
private static void readIndexedColorFormats() {
NodeList indexedColorTags = tmspec.getElementsByTagName("indexedcolor");
for (int i=0; i<indexedColorTags.getLength(); i++) {
Element ic = (Element)indexedColorTags.item(i);
String id = ic.getAttribute("id");
int bpp = Integer.parseInt(ic.getAttribute("bpp"));
String desc = XMLParser.getNodeValue(ic.getElementsByTagName("description").item(0));
int endianness = ic.getAttribute("endianness").equals("little") ? ColorCodec.LITTLE_ENDIAN : ColorCodec.BIG_ENDIAN;
String hexString = XMLParser.getNodeValue(ic.getElementsByTagName("data").item(0));
byte[] data = HexStringConverter.hexStringToBytes(hexString);
// pack the data into array of 32-bit ARGB ints
DirectColorCodec codec = new DirectColorCodec("", 24, 0xFF0000, 0x00FF00, 0x0000FF, 0, "");
codec.setEndianness(endianness);
int[] entries = new int[data.length / codec.getBytesPerPixel()];
for (int j=0; j<entries.length; j++) {
entries[j] = codec.fromBytes(data, j * codec.getBytesPerPixel());
}
colorcodecs.add(new IndexedColorCodec(id, bpp, entries, desc));
}
}
/**
*
* Reads the planartile format tags and creates planartilecodecs for them.
*
**/
private static void readPlanarTileFormats() {
NodeList planarTileTags = tmspec.getElementsByTagName("planartile");
for (int i=0; i<planarTileTags.getLength(); i++) {
Element plc = (Element)planarTileTags.item(i);
String id = plc.getAttribute("id");
int bpp = Integer.parseInt(plc.getAttribute("bpp"));
String desc = XMLParser.getNodeValue(plc.getElementsByTagName("description").item(0));
StringTokenizer st = new StringTokenizer(plc.getAttribute("planeorder"), ",");
int[] ofs = new int[st.countTokens()];
for (int j=0; j<ofs.length; j++) {
ofs[j] = Integer.parseInt(st.nextToken());
}
tilecodecs.add(new PlanarTileCodec(id, ofs, desc));
}
}
/**
*
* Reads the lineartile tags and creates lineartilecodecs for them.
*
**/
private static void readLinearTileFormats() {
NodeList linearTileTags = tmspec.getElementsByTagName("lineartile");
for (int i=0; i<linearTileTags.getLength(); i++) {
Element lnc = (Element)linearTileTags.item(i);
String id = lnc.getAttribute("id");
int bpp = Integer.parseInt(lnc.getAttribute("bpp"));
int ordering = LinearTileCodec.IN_ORDER;
if(lnc.getAttribute("ordering").equals("reverse"))
ordering = LinearTileCodec.REVERSE_ORDER;
String desc = XMLParser.getNodeValue(lnc.getElementsByTagName("description").item(0));
tilecodecs.add(new LinearTileCodec(id, bpp, ordering, desc));
}
}
/**
*
* Reads the directcolortile tags and creates directcolortilecodecs for them.
*
**/
private static void readDirectColorTileFormats() {
NodeList directColorTileTags = tmspec.getElementsByTagName("directcolortile");
for (int i=0; i<directColorTileTags.getLength(); i++) {
Element dcc = (Element)directColorTileTags.item(i);
String id = dcc.getAttribute("id");
// String formatID = dcc.getAttribute("colorformat"); TODO
int bpp = Integer.parseInt(dcc.getAttribute("bpp"));
int rmask = (int)Long.parseLong(dcc.getAttribute("rmask"), 16);
int gmask = (int)Long.parseLong(dcc.getAttribute("gmask"), 16);
int bmask = (int)Long.parseLong(dcc.getAttribute("bmask"), 16);
int amask = 0;
if (!dcc.getAttribute("amask").equals(""))
amask = (int)Long.parseLong(dcc.getAttribute("amask"), 16);
String desc = XMLParser.getNodeValue(dcc.getElementsByTagName("description").item(0));
tilecodecs.add(new DirectColorTileCodec(id, bpp, rmask, gmask, bmask, amask, desc));
}
}
/**
*
* Reads the compositetile tags and creates compositetilecodecs for them.
*
**/
private static void readCompositeTileFormats() {
NodeList compositeTileTags = tmspec.getElementsByTagName("compositetile");
for (int i=0; i<compositeTileTags.getLength(); i++) {
Element cpc = (Element)compositeTileTags.item(i);
String id = cpc.getAttribute("id");
String desc = XMLParser.getNodeValue(cpc.getElementsByTagName("description").item(0));
// build array of planar codecs
StringTokenizer st = new StringTokenizer(cpc.getAttribute("formats"), ",");
TileCodec[] codecs = new TileCodec[st.countTokens()];
int cc=0;
int bpp=0;
for (int j=0; j<codecs.length; j++) {
String cid = st.nextToken();
// find the codec with correct id
for (int k=0; k<tilecodecs.size(); k++) {
TileCodec tc = (TileCodec)tilecodecs.get(k);
if (tc.getID().equals(cid)) {
codecs[j] = tc;
cc++;
bpp += tc.getBitsPerPixel();
break;
}
}
}
if (cc != codecs.length) continue; // one or more codec IDs invalid
tilecodecs.add(new CompositeTileCodec(id, bpp, codecs, desc));
}
}
/**
*
* Reads the filefilter tags and creates TMTileCodecFilters for them.
*
**/
private static void readFileFilters() {
NodeList fftags = tmspec.getElementsByTagName("filefilter");
for (int i=0; i<fftags.getLength(); i++) {
Element ff = (Element)fftags.item(i);
String extlist = ff.getAttribute("extensions");
String desc = XMLParser.getNodeValue(ff.getElementsByTagName("description").item(0));
String codecID = ff.getAttribute("tileformat");
int defaultMode = TileCodec.MODE_1D;
if (ff.getAttribute("mode").equals("2D"))
defaultMode = TileCodec.MODE_2D;
filefilters.add(new TMTileCodecFileFilter(extlist, desc, codecID, defaultMode));
}
}
/**
*
* Reads the palettefilter tags and creates TMPaletteFilters for them.
*
**/
private static void readPaletteFilters() {
NodeList pftags = tmspec.getElementsByTagName("palettefilter");
for (int i=0; i<pftags.getLength(); i++) {
Element pf = (Element)pftags.item(i);
String extlist = pf.getAttribute("extensions");
String desc = XMLParser.getNodeValue(pf.getElementsByTagName("description").item(0));
String codecID = pf.getAttribute("colorformat");
int size = Integer.parseInt(pf.getAttribute("size"));
int offset = Integer.parseInt(pf.getAttribute("offset"));
int endianness = ColorCodec.LITTLE_ENDIAN;
if (pf.getAttribute("endianness").equals("big")) {
endianness = ColorCodec.BIG_ENDIAN;
}
palettefilters.add(new TMPaletteFileFilter(extlist, desc, codecID, size, offset, endianness));
}
}
/**
*
* Reads the filelistener tags and creates TMFileListeners for them.
*
**/
private static void readFileListeners() {
ClassLoader loader = ClassLoader.getSystemClassLoader();
NodeList fltags = tmspec.getElementsByTagName("filelistener");
for (int i=0; i<fltags.getLength(); i++) {
Element fl = (Element)fltags.item(i);
String extlist = fl.getAttribute("extensions");
String classname = fl.getAttribute("classname");
Class c;
try {
c = loader.loadClass(classname);
}
catch (Exception e) {
continue;
}
Object o;
try {
o = c.newInstance();
}
catch (Exception e) {
continue;
}
filelisteners.add(o);
}
}
/**
*
* Gets the vector of color codecs that's been created based on the XML.
*
**/
public static Vector getColorCodecs() {
return colorcodecs;
}
/**
*
* Gets the vector of tile codecs that's been created based on the XML.
*
**/
public static Vector getTileCodecs() {
return tilecodecs;
}
/**
*
* Gets the vector of file filters that's been created based on the XML.
*
**/
public static Vector getFileFilters() {
return filefilters;
}
/**
*
* Gets the vector of palette filters that's been created based on the XML.
*
**/
public static Vector getPaletteFilters() {
return palettefilters;
}
/**
*
* Gets the vector of file listeners that's been created based on the XML.
*
**/
public static Vector getFileListeners() {
return filelisteners;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,467 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.canvases;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
/**
*
* Provides a surface where decoded graphics data can be rendered.
* This is an abstract class: Subclasses are required to implement the
* unpackPixels() and packPixels() methods for decoding and encoding
* graphics data, respectively.
* Pixels must be decoded to 32-bit ARGB format (see implementation details below).
*
**/
public abstract class TMPixelCanvas extends JPanel {
protected int canvasWidth;
protected int canvasHeight;
protected double scale;
protected byte[] bits; // encoded data buffer
protected int offset; // starting offset in buffer
protected int[] pixels;
protected MemoryImageSource source=null;
private Image image;
private DirectColorModel colorModel;
private boolean showPixelGrid=false;
/**
*
* Creates a pixel pane using <code>bits</code> as the graphics source.
*
**/
public TMPixelCanvas(byte[] bits) {
super();
this.bits = bits;
setOffset(0);
setLayout(null);
colorModel = new DirectColorModel(32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000);
setBackground(Color.gray);
setCanvasSize(0, 0);
setScale(1.0);
}
/**
*
* Creates a pixel pane of the specified size and using <code>bits</code> as the graphics source.
*
**/
public TMPixelCanvas(byte[] bits, int canvasWidth, int canvasHeight) {
super();
this.bits = bits;
setOffset(0);
setLayout(null);
colorModel = new DirectColorModel(32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000);
setBackground(Color.gray);
setCanvasSize(canvasWidth, canvasHeight);
setScale(1.0);
}
/**
*
* Sets the size of the canvas in pixels.
*
**/
public void setCanvasSize(int canvasWidth, int canvasHeight) {
this.canvasWidth = canvasWidth;
this.canvasHeight = canvasHeight;
// create canvas image and initialize stuff
pixels = new int[canvasWidth*canvasHeight];
source = new MemoryImageSource(canvasWidth, canvasHeight, colorModel, pixels, 0, canvasWidth);
source.setAnimated(true);
image = createImage(source);
setScale(scale);
}
/**
*
* Sets the offset into the buffer from which tile data is unpacked/packed.
*
**/
public void setOffset(int offset) {
this.offset = offset;
}
/**
*
* Gets the offset.
*
**/
public int getOffset() {
return offset;
}
/**
*
* Sets the scale.
*
**/
public void setScale(double scale) {
if (scale < 1.0) scale = 1.0; // minimum
else if (scale > 32.0) scale = 32.0; // maximum
this.scale = scale;
// set size
int scaledWidth = (int)(canvasWidth*scale);
int scaledHeight = (int)(canvasHeight*scale);
setSize(scaledWidth, scaledHeight);
}
/**
*
* Gets the scale.
*
**/
public double getScale() {
return scale;
}
/**
*
* Subclasses required to implement this method. It is responsible for
* filling the canvas with pixels, by decoding data starting at
* &bits[offset].
*
**/
protected abstract void unpackPixels();
/**
*
* Subclasses required to implement this method. It is responsible for
* encoding the canvas's pixels into some format.
*
**/
protected abstract void packPixels();
/**
*
* Paints pixels and grid(s).
*
**/
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
// draw gridlines if necessary
if (showPixelGrid) {
drawPixelGrid(g);
}
}
/**
*
* Draws pixel grid.
*
**/
private void drawPixelGrid(Graphics g) {
if (scale < 8.0) return; // don't show it for scales less than 8
g.setColor(Color.gray);
// draw horizontal lines
for (int i=1; i<canvasHeight; i++) {
g.fillRect(0, (int)(i*scale), getWidth(), 1);
}
// draw vertical lines
for (int i=1; i<canvasWidth; i++) {
g.fillRect((int)(i*scale), 0, 1, getHeight());
}
}
/**
*
* Turns the pixel grid on (true) or off (false).
*
**/
public void setPixelGridVisible(boolean showPixelGrid) {
this.showPixelGrid = showPixelGrid;
}
/**
*
* Returns pixel grid visibility.
*
**/
public boolean isPixelGridVisible() {
return showPixelGrid;
}
/**
*
* Sets the pixel at location (x, y) in the image to the specified value.
*
**/
public void setPixel(int x, int y, int argb) {
pixels[(y*canvasWidth)+x] = argb;
}
/**
*
* Gets the pixel at location (x, y) in the image.
*
**/
protected int getPixel(int x, int y) {
return pixels[(y*canvasWidth)+x];
}
/**
*
* Inverts the pixel at (x,y).
*
**/
protected void xorPixel(int x, int y) {
setPixel(x, y, getPixel(x, y) ^ 0xFFFFFF);
}
/**
*
* Gets the buffer containing the currently rendered pixels.
*
**/
protected int[] getPixels() {
return pixels;
}
/**
*
* Gets the width of the canvas image.
*
**/
public int getCanvasWidth() {
return canvasWidth;
}
/**
*
* Gets the height of the canvas image.
*
**/
public int getCanvasHeight() {
return canvasHeight;
}
/**
*
* Sets the data buffer that will be used to fetch/encode native graphics data.
*
**/
public void setBits(byte[] bits) {
this.bits = bits;
}
/**
*
* Gets the native graphics data buffer.
*
**/
public byte[] getBits() {
return bits;
}
/**
*
* Updates the pixels and repaints the canvas.
*
**/
public void redraw() {
source.newPixels();
repaint();
}
/**
*
* Gets the image where the pixels are rendered.
*
**/
public Image getImage() {
return image;
}
/**
*
* Mirrors the canvas (flips horizontally).
*
**/
public void mirror() {
for (int y=0; y<canvasHeight; y++) {
for (int x=0; x<canvasWidth/2; x++) {
int p1 = getPixel(x, y);
int p2 = getPixel(canvasWidth-1-x, y);
setPixel(x, y, p2);
setPixel(canvasWidth-1-x, y, p1);
}
}
packPixels();
}
/**
*
* Flips the canvas (vertically).
*
**/
public void flip() {
for (int y=0; y<canvasHeight/2; y++) {
for (int x=0; x<canvasWidth; x++) {
int p1 = getPixel(x, y);
int p2 = getPixel(x, canvasHeight-1-y);
setPixel(x, y, p2);
setPixel(x, canvasHeight-1-y, p1);
}
}
packPixels();
}
/**
*
* Rotates the canvas 90 degrees left (counter-clockwise).
*
**/
public void rotateLeft() {
int[] pix = getPixels();
setCanvasSize(canvasHeight, canvasWidth);
int ofs = 0;
for (int i=0; i<canvasWidth; i++) {
for (int j=canvasHeight-1; j>=0; j--) {
setPixel(i, j, pix[ofs++]);
}
}
packPixels();
setScale(scale);
}
/**
*
* Rotates the canvas 90 degrees right (clockwise).
*
**/
public void rotateRight() {
int[] pix = getPixels();
setCanvasSize(canvasHeight, canvasWidth);
int ofs = 0;
for (int i=canvasWidth-1; i>=0; i--) {
for (int j=0; j<canvasHeight; j++) {
setPixel(i, j, pix[ofs++]);
}
}
packPixels();
setScale(scale);
}
/**
*
* Shifts the canvas one column left.
*
**/
public void shiftLeft() {
for (int y=0; y<canvasHeight; y++) {
int p0 = getPixel(0, y);
for (int x=0; x<canvasWidth-1; x++) {
setPixel(x, y, getPixel(x+1, y));
}
setPixel(canvasWidth-1, y, p0);
}
packPixels();
}
/**
*
* Shifts the canvas one column right.
*
**/
public void shiftRight() {
for (int y=0; y<canvasHeight; y++) {
int p0 = getPixel(canvasWidth-1, y);
for (int x=canvasWidth-2; x>=0; x--) {
setPixel(x+1, y, getPixel(x, y));
}
setPixel(0, y, p0);
}
packPixels();
}
/**
*
* Shifts the canvas one row up.
*
**/
public void shiftUp() {
for (int x=0; x<canvasWidth; x++) {
int p0 = getPixel(x, 0);
for (int y=0; y<canvasHeight-1; y++) {
setPixel(x, y, getPixel(x, y+1));
}
setPixel(x, canvasHeight-1, p0);
}
packPixels();
}
/**
*
* Shifts the canvas one row down.
*
**/
public void shiftDown() {
for (int x=0; x<canvasWidth; x++) {
int p0 = getPixel(x, canvasHeight-1);
for (int y=canvasHeight-2; y>=0; y--) {
setPixel(x, y+1, getPixel(x, y));
}
setPixel(x, 0, p0);
}
packPixels();
}
}

View file

@ -0,0 +1,354 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.canvases;
import tm.TMPalette;
import tm.tilecodecs.TileCodec;
import tm.reversibleaction.ReversibleMoveSelectionAction;
import tm.ui.TMUI;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
/**
*
* A tile canvas that can be moved around.
*
**/
public class TMSelectionCanvas extends TMTileCanvas implements MouseInputListener {
private TMUI ui;
private static JPopupMenu selectionPopup = null;
private int dx;
private int dy;
private int oldX;
private int oldY;
/**
*
* Creates a selection canvas by copying the specified tile grid.
*
**/
public TMSelectionCanvas(TMUI ui, TMTileCanvas canvas, int x1, int y1, int w, int h) {
super(null);
this.ui = ui;
if (selectionPopup == null)
initSelectionPopup();
addMouseListener(this);
addMouseMotionListener(this);
setMode(TileCodec.MODE_1D); // all copied data is stored in 1-Dimensional mode
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
setVisible(false);
// create buffer to hold tile data
byte[] selbits = new byte[w * h * canvas.getCodec().getTileSize()];
// init the canvas
setBits(selbits);
setGridSize(w, h);
setCodec(canvas.getCodec());
if (canvas.getPalette() != null) {
setPalette(new TMPalette(canvas.getPalette()));
setPalIndex(canvas.getPalIndex());
}
// copy pixels
for (int i=0; i<h*8; i++) {
for (int j=0; j<w*8; j++) {
setPixel(j, i, canvas.getPixel(j+x1*8, i+y1*8));
}
}
packPixels();
}
/**
*
* Sets up the selection popup menu.
*
**/
private void initSelectionPopup() {
selectionPopup = new JPopupMenu();
JMenuItem menuItem;
// Cut
menuItem = new JMenuItem("Cut");
menuItem.setMnemonic(KeyEvent.VK_T);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doCutCommand();
}
}
);
selectionPopup.add(menuItem);
// Copy
menuItem = new JMenuItem("Copy");
menuItem.setMnemonic(KeyEvent.VK_C);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doCopyCommand();
}
}
);
selectionPopup.add(menuItem);
// Paste
menuItem = new JMenuItem("Paste");
menuItem.setMnemonic(KeyEvent.VK_P);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doPasteCommand();
}
}
);
selectionPopup.add(menuItem);
// Clear
menuItem = new JMenuItem("Clear");
menuItem.setMnemonic(KeyEvent.VK_L);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doClearCommand();
}
}
);
selectionPopup.add(menuItem);
// Select All
menuItem = new JMenuItem("Select All");
menuItem.setMnemonic(KeyEvent.VK_S);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doSelectAllCommand();
}
}
);
selectionPopup.add(menuItem);
//
selectionPopup.addSeparator();
// Mirror
menuItem = new JMenuItem("Mirror");
menuItem.setMnemonic(KeyEvent.VK_M);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doMirrorCommand();
}
}
);
selectionPopup.add(menuItem);
// Flip
menuItem = new JMenuItem("Flip");
menuItem.setMnemonic(KeyEvent.VK_F);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doFlipCommand();
}
}
);
selectionPopup.add(menuItem);
//
selectionPopup.addSeparator();
// Rotate Right
menuItem = new JMenuItem("Rotate Right");
menuItem.setMnemonic(KeyEvent.VK_O);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doRotateRightCommand();
}
}
);
selectionPopup.add(menuItem);
// Rotate Left
menuItem = new JMenuItem("Rotate Left");
menuItem.setMnemonic(KeyEvent.VK_A);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doRotateLeftCommand();
}
}
);
selectionPopup.add(menuItem);
//
selectionPopup.addSeparator();
// Shift Left
menuItem = new JMenuItem("Shift Left");
menuItem.setMnemonic(KeyEvent.VK_L);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doShiftLeftCommand();
}
}
);
selectionPopup.add(menuItem);
// Shift Right
menuItem = new JMenuItem("Shift Right");
menuItem.setMnemonic(KeyEvent.VK_R);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doShiftRightCommand();
}
}
);
selectionPopup.add(menuItem);
// Shift Up
menuItem = new JMenuItem("Shift Up");
menuItem.setMnemonic(KeyEvent.VK_U);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doShiftUpCommand();
}
}
);
selectionPopup.add(menuItem);
// Shift Down
menuItem = new JMenuItem("Shift Down");
menuItem.setMnemonic(KeyEvent.VK_D);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doShiftDownCommand();
}
}
);
selectionPopup.add(menuItem);
//
selectionPopup.addSeparator();
// Stretch...
menuItem = new JMenuItem("Stretch...");
menuItem.setMnemonic(KeyEvent.VK_E);
menuItem.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
ui.doStretchCommand();
}
}
);
selectionPopup.add(menuItem);
}
/**
*
* Paints the selection.
*
**/
public void paintComponent(Graphics g) {
super.paintComponent(g);
// draw frame
g.setColor(Color.white);
g.drawRect(0, 0, getWidth()-1, getHeight()-1);
}
/**
*
* Called when user has pressed mouse button and is moving mouse.
*
**/
public void mouseDragged(MouseEvent e) {
// reposition
setLocation(getX()+e.getX()-dx, getY()+e.getY()-dy);
}
/**
*
* Mouse event handlers.
*
**/
public void mouseMoved(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mousePressed(MouseEvent e) {
dx = e.getX();
dy = e.getY();
oldX = getX();
oldY = getY();
}
public void mouseReleased(MouseEvent e) {
if (hasMoved()) {
snapToGrid();
int dim = getScaledTileDim();
ui.getSelectedView().addReversibleAction(
new ReversibleMoveSelectionAction(
this,
oldX / dim,
oldY / dim,
getX() / dim,
getY() / dim
)
);
}
maybeShowPopup(e);
}
/**
*
* Checks if the selection has been moved
*
**/
public boolean hasMoved()
{
return (oldX != getX() && oldY != getY());
}
/**
*
* Aligns the selection to a tile boundary.
*
**/
public void snapToGrid() {
int dim = getScaledTileDim();
// snap to atomic tile grid
int x = getX();
int y = getY();
setLocation((x / dim) * dim, (y / dim) * dim); //TODO: fix the snapping
}
/**
*
* Shows the popup menu if the mouse event was a popup trigger.
*
**/
public void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
selectionPopup.show(e.getComponent(), e.getX(), e.getY());
}
}
}

View file

@ -0,0 +1,583 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.canvases;
import tm.TMPalette;
import tm.tilecodecs.TileCodec;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
/**
*
* A tile canvas is a surface where a grid of atomic tiles can be rendered.
*
**/
public class TMTileCanvas extends TMPixelCanvas {
protected int mode=TileCodec.MODE_1D;
protected int[] pixdata = new int[8*8];
protected int cols=0;
protected int rows=0;
protected TileCodec codec=null;
protected TMPalette palette=null;
protected int palIndex=0;
private boolean showTileGrid=false;
/**
*
* Creates a tile pane, with <code>bits</code> as the encoded tile data source.
*
**/
public TMTileCanvas(byte[] bits) {
super(bits);
this.cols = 0;
this.rows = 0;
}
/**
*
* Creates a tile pane of the specified size, and with <code>bits</code>
* as the encoded tile data source.
* Doesn't work quite right?
**/
public TMTileCanvas(byte[] bits, int cols, int rows) {
super(bits, cols*8, rows*8);
this.cols = cols;
this.rows = rows;
}
/**
*
* Sets the tile mode. This determines how tile data is interpreted.
* Valid modes are MODE_1D and MODE_2D.
*
**/
public void setMode(int mode) {
this.mode = mode;
}
/**
*
* Gets the mode.
*
**/
public int getMode() {
return mode;
}
/**
*
* Sets the codec that's used for decoding+encoding individual tiles.
*
**/
public void setCodec(TileCodec codec) {
this.codec = codec;
}
/**
*
* Gets the tile codec.
*
**/
public TileCodec getCodec() {
return codec;
}
/**
*
* Sets the palette that's used for mapping colors when bitsPerPixel <= 8.
*
**/
public void setPalette(TMPalette palette) {
this.palette = palette;
}
/**
*
* Gets the palette.
*
**/
public TMPalette getPalette() {
return palette;
}
/**
*
* Sets the palette index.
*
**/
public void setPalIndex(int palIndex) {
this.palIndex = palIndex;
}
/**
*
* Gets the palette index.
*
**/
public int getPalIndex() {
return palIndex;
}
/**
*
* Sets the size of the tile canvas.
*
**/
public void setGridSize(int cols, int rows) {
setCanvasSize(cols*8, rows*8);
this.cols = cols;
this.rows = rows;
}
/**
*
* Encodes the specified tile. TODO
*
**/
public void packTile(int x, int y) {
int pixOfs;
int bitsOfs, pos;
// encode single atomic tile
bitsOfs = getTileBitsOffset(x, y);
if (bitsOfs >= 0) {
// copy pixels
pixOfs = (y * 8 * canvasWidth) + (x * 8);
pos = 0;
if (codec.getBitsPerPixel() <= 8) {
int colorCount = codec.getColorCount();
int colorIndex = palIndex * colorCount;
// map RGB values to palette indices
for (int p=0; p<8; p++) {
for (int q=0; q<8; q++) {
pixdata[pos++] = palette.indexOf(colorIndex, pixels[pixOfs++]);
}
pixOfs += canvasWidth - 8;
}
}
else {
// non-palettized: color is actual 32-bit ARGB value
for (int p=0; p<8; p++) {
for (int q=0; q<8; q++) {
pixdata[pos++] = pixels[pixOfs++];
}
pixOfs += canvasWidth - 8;
}
}
codec.encode(pixdata, bits, bitsOfs, getStride());
}
else {
// not valid tile, do nothing
}
}
/**
*
* Gets the number of columns in the tile grid.
*
**/
public int getCols() {
return cols;
}
/**
*
* Gets the number of rows in the tile grid.
*
**/
public int getRows() {
return rows;
}
/**
*
* Decodes tile data to pixel buffer.
*
**/
public void unpackPixels() {
if (codec == null) return;
int[] decodedTile;
int pixOfs = 0;
int bitsOfs, tileOfs, pos;
int colorIndex = palIndex * codec.getColorCount(); // only valid for palettized codecs
int bpp = codec.getBitsPerPixel();
int stride = getStride();
// render grid of atomic tiles
for (int i=0; i<rows; i++) {
for (int j=0; j<cols; j++) {
bitsOfs = getTileBitsOffset(j, i);
if (bitsOfs >= 0) {
decodedTile = codec.decode(bits, bitsOfs, stride);
// copy pixels
tileOfs = pixOfs;
pos = 0;
if ((bpp <= 8) && (palette != null)) {
// map palette indices to RGB values
for (int p=0; p<8; p++) {
for (int q=0; q<8; q++) {
pixels[tileOfs++] = palette.getEntryRGB(colorIndex + decodedTile[pos++]);
}
tileOfs += canvasWidth - 8;
}
}
else {
// non-palettized: color is actual 32-bit ARGB value
for (int p=0; p<8; p++) {
for (int q=0; q<8; q++) {
pixels[tileOfs++] = decodedTile[pos++];
}
tileOfs += canvasWidth - 8;
}
}
}
else {
// not valid tile, fill with gray pixels
tileOfs = pixOfs;
for (int p=0; p<8; p++) {
for (int q=0; q<8; q++) {
pixels[tileOfs++] = 0x7F7F7F;
}
tileOfs += canvasWidth - 8;
}
}
pixOfs += 8; // next tile column
}
pixOfs += canvasWidth*7; // next tile row
}
source.newPixels();
}
/**
*
* Encodes tile data.
*
**/
public void packPixels() {
if (codec == null) return;
int pixOfs = 0;
int bitsOfs, tileOfs, pos;
int colorCount = codec.getColorCount(); // only valid for palettized codecs
int colorIndex = palIndex * colorCount;
int bpp = codec.getBitsPerPixel();
int stride = getStride();
// encode grid of atomic tiles
for (int i=0; i<rows; i++) {
for (int j=0; j<cols; j++) {
bitsOfs = getTileBitsOffset(j, i);
if (bitsOfs >= 0) {
// copy pixels
tileOfs = pixOfs;
pos = 0;
if ((bpp <= 8) && (palette != null)) {
// map RGB values to palette indices
for (int p=0; p<8; p++) {
for (int q=0; q<8; q++) {
pixdata[pos++] = palette.indexOf(colorIndex, pixels[tileOfs++]);
}
tileOfs += canvasWidth - 8;
}
}
else {
// non-palettized: color is actual 32-bit ARGB value
for (int p=0; p<8; p++) {
for (int q=0; q<8; q++) {
pixdata[pos++] = pixels[tileOfs++];
}
tileOfs += canvasWidth - 8;
}
}
codec.encode(pixdata, bits, bitsOfs, stride);
}
else {
// not valid tile, do nothing
}
pixOfs += 8; // next tile column
}
pixOfs += canvasWidth*7; // next tile row
}
}
/**
*
* Paints tiles and grid.
*
**/
public void paintComponent(Graphics g) {
super.paintComponent(g);
// draw gridlines if necessary
if (showTileGrid) {
drawTileGrid(g);
}
}
/**
*
* Draws atomic tile grid.
*
**/
private void drawTileGrid(Graphics g) {
g.setColor(Color.red); // gridline color
// draw horizontal lines
for (int i=1; i<rows; i++) {
g.fillRect(0, (int)(i*scale*8), getWidth(), 1);
}
// draw vertical lines
for (int i=1; i<cols; i++) {
g.fillRect((int)(i*scale*8), 0, 1, getHeight());
}
}
/**
*
* Gets the length of a side (in pixels) in an 8x8 tile, subject to the current scaling.
*
**/
public int getScaledTileDim() {
return (int)(scale*8);
}
/**
*
* Turns the tile grid on (true) or off (false).
*
**/
public void setTileGridVisible(boolean showTileGrid) {
this.showTileGrid = showTileGrid;
}
/**
*
* Returns tile grid visibility.
*
**/
public boolean isTileGridVisible() {
return showTileGrid;
}
/**
*
* Gets the starting offset of the data for the tile at position (x,y)
* in the grid.
*
**/
protected int getTileBitsOffset(int x, int y) {
int relOfs = (y * getRowSize()) + (x * getTileIncrement());
int absOfs = relOfs + offset;
// range check
int limit = 0;
if (mode == TileCodec.MODE_1D) {
limit = bits.length - codec.getTileSize();
}
else {
limit = bits.length - getRowIncrement();
}
if (absOfs <= limit) return absOfs;
return -1;
}
/**
*
* Gets the size of one row of tiles.
*
**/
public int getRowSize() {
if (codec != null)
return cols * codec.getTileSize();
else
return 0;
}
/**
*
* Gets the size of one page of tiles (entire tile grid).
*
**/
public int getPageSize() {
if (codec != null)
return rows * getRowSize();
else
return 0;
}
/**
*
* Gets the amount to increment data offset per tile.
*
**/
public int getTileIncrement() {
return (mode == TileCodec.MODE_1D) ? codec.getTileSize() : codec.getBytesPerRow();
}
/**
*
* Gets the amount to increment data offset per row.
*
**/
public int getRowIncrement() {
return codec.getTileSize() * cols;
}
/**
*
* Gets the amount to increment data offset per page.
*
**/
public int getPageIncrement() {
return getRowIncrement() * rows;
}
/**
*
* Gets the stride.
*
**/
public int getStride() {
return (mode == TileCodec.MODE_1D) ? 0 : cols-1;
}
/**
*
* Stretches the canvas from current (cols,rows) to specified (cols,rows).
*
**/
public void stretchTo(int cols, int rows) {
// get current pixels & dimensions
int[] pix = getPixels();
int oldW = getCanvasWidth();
int oldH = getCanvasHeight();
// set new size
setGridSize(cols, rows);
int w = getCanvasWidth();
int h = getCanvasHeight();
// create buffer to hold tile data
byte[] selbits = new byte[cols * rows * codec.getTileSize()];
setBits(selbits);
// calculate step size for each dimension
double stepX = (double)oldW / (double)w;
double stepY = (double)oldH / (double)h;
// stretch the old image onto the new canvas
double x;
double y=0;
for (int i=0; i<h; i++) {
x=0;
for (int j=0; j<w; j++) {
int p = pix[(((int)y)*oldW)+(int)x];
setPixel(j, i, p);
x += stepX;
}
y += stepY;
}
packPixels();
}
/**
*
* Rotates the grid of tiles 90 degrees left (counter-clockwise).
*
**/
public void rotateLeft() {
int[] pix = getPixels();
setGridSize(rows, cols);
int ofs = 0;
for (int i=0; i<canvasWidth; i++) {
for (int j=canvasHeight-1; j>=0; j--) {
setPixel(i, j, pix[ofs++]);
}
}
packPixels();
setScale(scale);
}
/**
*
* Rotates the grid of tiles 90 degrees right (clockwise).
*
**/
public void rotateRight() {
int[] pix = getPixels();
setGridSize(rows, cols);
int ofs = 0;
for (int i=canvasWidth-1; i>=0; i--) {
for (int j=0; j<canvasHeight; j++) {
setPixel(i, j, pix[ofs++]);
}
}
packPixels();
setScale(scale);
}
/**
*
* Copies a tile.
*
**/
public void copyTile(byte[] from, byte[] to, int offsetFrom, int offsetTo, int strideFrom, int strideTo, int bytesPerRow) {
for (int i=0; i<8; i++) {
// copy one row
for (int j=0; j<bytesPerRow; j++) {
to[offsetTo++] = from[offsetFrom++];
}
offsetFrom += strideFrom;
offsetTo += strideTo;
}
}
}

View file

@ -0,0 +1,186 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.colorcodecs;
/**
*
* Abstract superclass for color codecs.
* A "color codec" defines how values of a particular format are translated
* to 32-bit ARGB pixel values (decoded), and back again (encoded).
*
**/
public abstract class ColorCodec {
public static final int LITTLE_ENDIAN=1;
public static final int BIG_ENDIAN=2;
public static final int MIDDLE_ENDIAN=3;
protected int bitsPerPixel;
protected int bytesPerPixel;
protected String id;
protected String description;
private static int endianness;
private int startShift;
private int shiftStep;
/**
*
* Creates a ColorCodec with the given id, bits per pixel and description.
*
**/
public ColorCodec(String id, int bitsPerPixel, String description) {
this.id = id;
this.bitsPerPixel = bitsPerPixel;
this.description = description;
bytesPerPixel = getBytesRequired(bitsPerPixel);
setEndianness(LITTLE_ENDIAN); // default
}
/**
* Sets the endianness.
* It determines the byte order related to methods toBytes() and fromBytes().
**/
public void setEndianness(int endianness) {
this.endianness = endianness;
if (endianness == LITTLE_ENDIAN) {
startShift = 0;
shiftStep = 8;
}
else {
// BIG_ENDIAN
startShift = (bytesPerPixel-1) * 8;
shiftStep = -8;
}
}
/**
*
* Gets the ID.
*
**/
public String getID() {
return id;
}
/**
*
* Gets the number of bits per pixel.
*
**/
public int getBitsPerPixel() {
return bitsPerPixel;
}
/**
*
* Gets the number of bytes required to hold one pixel.
*
**/
public int getBytesPerPixel() {
return bytesPerPixel;
}
/**
*
* Gets the description.
*
**/
public String getDescription() {
return description;
}
/**
*
* This method takes a value assumed to be in the color codec's native format
* and returns the equivalent 32-bit ARGB value.
*
**/
public abstract int decode(int value);
/**
*
* This method takes a value assumed to be in 32-bit ARGB format and returns
* the equivalent native format value.
*
**/
public abstract int encode(int argb);
/**
*
* Converts given value to a series of bytes, according to current endianness.
* @param bytes Array to store resulting bytes in
* @param offset Starting offset in array
*
**/
public byte[] toBytes(int value, byte[] bytes, int offset) {
int shift = startShift;
for (int i=0; i<bytesPerPixel; i++) {
bytes[offset+i] = (byte)(value >> shift);
shift += shiftStep;
}
return bytes;
}
/**
*
* Converts bytes from given array to a value, according to current endianness.
* @param offset Where to start reading the bytes that make up the value.
*
**/
public int fromBytes(byte[] bytes, int offset) {
int shift = startShift;
int value = 0;
for (int i=0; i<bytesPerPixel; i++) {
value |= (bytes[offset+i] & 0xFF) << shift;
shift += shiftStep;
}
return value;
}
/**
*
* Gets the least number of whole bytes that are required to store
* <code>bits</code> bits of information.
*
**/
private static int getBytesRequired(int bits) {
int bytes = bits / 8;
int extrabits = bits % 8;
if (extrabits != 0) bytes++;
return bytes;
}
public String toString() {
return description;
}
}

View file

@ -0,0 +1,169 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.colorcodecs;
/**
*
* A DirectColorCodec translates pixels exactly the same way as
* a direct-color tile codec.
* The composition of a pixel is described by Red, Green,
* Blue and Alpha masks.
*
**/
public class DirectColorCodec extends ColorCodec {
private int rmask; // bitmask for Red component
private int gmask; // bitmask for Green component
private int bmask; // bitmask for Blue component
private int amask; // bitmask for Alpha component
// how much each component must be shifted to be transformed to a 32-bit ARGB int-packed Java pixel.
// these are pre-calculated in the constructor and used for decoding/encoding individual pixels.
private int rshift;
private int gshift;
private int bshift;
private int ashift;
public DirectColorCodec(String id, int bitsPerPixel, int rmask, int gmask, int bmask, int amask, String description) {
super(id, bitsPerPixel, description);
this.rmask = rmask;
this.gmask = gmask;
this.bmask = bmask;
this.amask = amask;
// calculate the shifts
rshift = 23 - msb(rmask);
gshift = 15 - msb(gmask);
bshift = 7 - msb(bmask);
ashift = 31 - msb(amask);
}
/**
* Gets the position of the most significant set bit in the given int.
**/
private static int msb(int mask) {
for (int i=31; i>=0; i--) {
if ((mask & 0x80000000) != 0) {
return i;
}
mask <<= 1;
}
return -1; // no bits set
}
/**
*
*
**/
public int decode(int value) {
int r, g, b, a;
// decode R component
r = value & rmask;
if (rshift < 0) {
r >>= -rshift;
}
else {
r <<= rshift;
}
// decode G component
g = value & gmask;
if (gshift < 0) {
g >>= -gshift;
}
else {
g <<= gshift;
}
// decode B component
b = value & bmask;
if (bshift < 0) {
b >>= -bshift;
}
else {
b <<= bshift;
}
// decode A component
a = value & amask;
if (ashift < 0) {
a >>= -ashift;
}
else {
a <<= ashift;
}
// final pixel
return (a | r | g | b);
}
/**
*
*
**/
public int encode(int argb) {
int r, g, b, a;
// encode R component
r = argb;
if (rshift < 0) {
r <<= -rshift;
}
else {
r >>= rshift;
}
r &= rmask;
// encode G component
g = argb;
if (gshift < 0) {
g <<= -gshift;
}
else {
g >>= gshift;
}
g &= gmask;
// encode B component
b = argb;
if (bshift < 0) {
b <<= -bshift;
}
else {
b >>= bshift;
}
b &= bmask;
// encode A component
a = argb;
if (ashift < 0) {
a <<= -ashift;
}
else {
a >>= ashift;
}
a &= amask;
// final value
return (a | r | g | b);
}
}

View file

@ -0,0 +1,75 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.colorcodecs;
/**
*
* An IndexedColorCodec translates pixels according to a
* pre-defined array of 32-bit ARGB values.
*
**/
public class IndexedColorCodec extends ColorCodec {
private int[] colorTable; // table of pre-defined ARGB values
public IndexedColorCodec(String id, int bitsPerPixel, int[] colorTable, String description) {
super(id, bitsPerPixel, description);
this.colorTable = colorTable;
}
/**
*
* To decode, simply look up the table.
*
**/
public int decode(int value) {
if (value < colorTable.length) {
return colorTable[value];
}
return 0; // undefined
}
/**
*
* To encode, find the closest match in the table.
*
**/
public int encode(int argb) {
int targetR = (argb & 0x00FF0000) >> 16;
int targetG = (argb & 0x0000FF00) >> 8;
int targetB = (argb & 0x000000FF);
int bestEntry=0, bestDiff=1000000;
for (int i=0; i<colorTable.length; i++) {
int val = colorTable[i];
int r = (val & 0x00FF0000) >> 16;
int g = (val & 0x0000FF00) >> 8;
int b = (val & 0x000000FF);
int diff = Math.abs(targetR - r) + Math.abs(targetG - g) + Math.abs(targetB - b);
if (diff < bestDiff) {
bestDiff = diff;
bestEntry = i;
}
}
return bestEntry;
}
}

View file

@ -0,0 +1,104 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.filelistener;
/**
*
* Listener for Game Boy Advance (*.gba) files.
*
**/
public class GameBoyAdvanceFileListener extends TMFileListener {
private static final int[] nintendoLogo = {
0x24,0xFF,0xAE,0x51,0x69,0x9A,0xA2,0x21,0x3D,0x84,0x82,0x0A,0x84,0xE4,0x09,0xAD,
0x11,0x24,0x8B,0x98,0xC0,0x81,0x7F,0x21,0xA3,0x52,0xBE,0x19,0x93,0x09,0xCE,0x20,
0x10,0x46,0x4A,0x4A,0xF8,0x27,0x31,0xEC,0x58,0xC7,0xE8,0x33,0x82,0xE3,0xCE,0xBF,
0x85,0xF4,0xDF,0x94,0xCE,0x4B,0x09,0xC1,0x94,0x56,0x8A,0xC0,0x13,0x72,0xA7,0xFC,
0x9F,0x84,0x4D,0x73,0xA3,0xCA,0x9A,0x61,0x58,0x97,0xA3,0x27,0xFC,0x03,0x98,0x76,
0x23,0x1D,0xC7,0x61,0x03,0x04,0xAE,0x56,0xBF,0x38,0x84,0x00,0x40,0xA7,0x0E,0xFD,
0xFF,0x52,0xFE,0x03,0x6F,0x95,0x30,0xF1,0x97,0xFB,0xC0,0x85,0x60,0xD6,0x80,0x25,
0xA9,0x63,0xBE,0x03,0x01,0x4E,0x38,0xE2,0xF9,0xA2,0x34,0xFF,0xBB,0x3E,0x03,0x44,
0x78,0x00,0x90,0xCB,0x88,0x11,0x3A,0x94,0x65,0xC0,0x7C,0x63,0x87,0xF0,0x3C,0xAF,
0xD6,0x25,0xE4,0x8B,0x38,0x0A,0xAC,0x72,0x21,0xD4,0xF8,0x07
} ;
/**
*
* Detects if this is a Game Boy Advance file.
* The critera are that the extension is gba, that the Nintendo logo
* character data is correct and that the byte at 0xB2 equals 0x96.
*
**/
public boolean doFormatDetect(final byte[] data, String extension) {
// verify extension
if (!extension.equals("gba")) {
return false;
}
// verify Nintendo Logo Character Data
for (int i=0; i<nintendoLogo.length; i++) {
if ((data[0x004+i] & 0xFF) != nintendoLogo[i]) {
return false;
}
}
if ((data[0xB2] & 0xFF) != 0x96) {
return false;
}
return true;
}
/**
*
* Does nothing on file load.
*
**/
public void fileLoaded(byte[] data, String extension) {
}
/**
*
* Recalculates the complement check and checksum on file save.
*
**/
public void fileSaving(byte[] data, String extension) {
// update complement check
int complementCheck = 0xE7;
for(int i=0xA0; i<=0xBC; i++) {
complementCheck -= data[i] & 0xFF;
}
data[0xBD] = (byte)(complementCheck & 0xFF);
// update checksum
int checkSum = 0;
data[0xBE] = 0;
data[0xBF] = 0;
for (int i=0; i<data.length; i++) {
checkSum += data[i] & 0xFF;
}
data[0xBE] = (byte)((checkSum >> 8) & 0xFF);
data[0xBF] = (byte)(checkSum & 0xFF);
}
}

View file

@ -0,0 +1,156 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.filelistener;
/**
*
* Listener for Game Boy (*.gb, *.gbc) files.
*
**/
public class GameBoyFileListener extends TMFileListener {
private static final int[] scrollingNintendoGraphic = {
0xCE,0xED,0x66,0x66,0xCC,0x0D,0x00,0x0B,0x03,0x73,0x00,0x83,0x00,0x0C,0x00,0x0D,
0x00,0x08,0x11,0x1F,0x88,0x89,0x00,0x0E,0xDC,0xCC,0x6E,0xE6,0xDD,0xDD,0xD9,0x99,
0xBB,0xBB,0x67,0x63,0x6E,0x0E,0xEC,0xCC,0xDD,0xDC,0x99,0x9F,0xBB,0xB9,0x33,0x3E
};
// cartridge types (TODO: add more types)
private static final int ROM_ONLY = 0x00;
private static final int ROM_MBC1 = 0x01;
private static final int ROM_MBC1_RAM = 0x02;
private static final int ROM_MBC1_RAM_BATTERY = 0x03;
private static final int ROM_MBC2 = 0x05;
private static final int ROM_MBC2_BATTERY = 0x06;
private static final int ROM_RAM = 0x08;
private static final int ROM_RAM_BATTERY = 0x09;
private static final int ROM_HUC1_RAM_BATTERY = 0xFF;
// cartridge sizes
private static final int SIZE_256K = 0x00;
private static final int SIZE_512K = 0x01;
private static final int SIZE_1M = 0x02;
private static final int SIZE_2M = 0x03;
private static final int SIZE_4M = 0x04;
private static final int SIZE_8M = 0x05;
private static final int SIZE_16M = 0x06;
private static final int SIZE_9M = 0x52;
private static final int SIZE_10M = 0x53;
private static final int SIZE_12M = 0x54;
/**
*
* Detects if this is a GameBoy file.
* The criteria is that the extension is either gb, gbc or sgb and that
* the scrolling Nintendo graphic data is correct.
*
**/
public boolean doFormatDetect(final byte[] data, String extension) {
// verify extension
if (!(extension.equals("gb")
|| extension.equals("gbc")
|| extension.equals("sgb"))) {
return false;
}
// verify scrolling Nintendo graphic
for (int i=0; i<scrollingNintendoGraphic.length; i++) {
if ((data[0x104+i] & 0xFF) != scrollingNintendoGraphic[i]) {
return false;
}
}
// verify cartridge type
/* int ct = data[0x147] & 0xFF;
switch (ct) {
case ROM_ONLY:
case ROM_MBC1:
case ROM_MBC1_RAM:
case ROM_MBC1_RAM_BATTERY:
case ROM_MBC2:
case ROM_MBC2_BATTERY:
case ROM_RAM:
case ROM_RAM_BATTERY:
case ROM_HUC1_RAM_BATTERY:
break;
default:
return false;
}
*/
// verify ROM size in header
int rs = data[0x148] & 0xFF;
switch (rs) {
case SIZE_256K:
case SIZE_512K:
case SIZE_1M:
case SIZE_2M:
case SIZE_4M:
case SIZE_8M:
case SIZE_16M:
case SIZE_9M:
case SIZE_10M:
case SIZE_12M:
break;
default:
return false;
}
// TODO: verify actual file size
return true;
}
/**
*
* Does nothing on file load.
*
**/
public void fileLoaded(byte[] data, String extension) {
}
/**
*
* Recalculates the complement check and checksum on file save.
*
**/
public void fileSaving(byte[] data, String extension) {
// update complement check
int r = 25;
for(int i=0x134; i<0x14D; i++) {
r += data[i] & 0xFF;
}
data[0x14D] = (byte)(0x100-r);
// update checksum
int checkSum = 0;
data[0x14E] = 0;
data[0x14F] = 0;
int len = data.length & 0x0FFF8000;
for (int i=0; i<len; i++) {
checkSum += data[i] & 0xFF;
}
data[0x14E] = (byte)((checkSum >> 8) & 0xFF);
data[0x14F] = (byte)(checkSum & 0xFF);
}
}

View file

@ -0,0 +1,50 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.filelistener;
/**
*
*
**/
public class INESFileListener extends TMFileListener {
private static int[] iNES_ID = { 0x4E,0x45,0x53,0x1A };
public boolean doFormatDetect(final byte[] data, String extension) {
// verify extension
if (!extension.equals("nes")) {
return false;
}
// verify NES^Z
for (int i=0; i<iNES_ID.length; i++) {
if ((data[i] & 0xFF) != iNES_ID[i]) {
return false;
}
}
return true;
}
public void fileLoaded(byte[] data, String extension) {
}
public void fileSaving(byte[] data, String extension) {
}
}

View file

@ -0,0 +1,176 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.filelistener;
/**
*
* Listener for Sega Genesis / Master System / 32X (*.smd) files.
*
**/
public class SegaGenesisFileListener extends TMFileListener {
private static final int[] sega_Mega_Drive_ = {
0x53,0x45,0x47,0x41,0x20,0x4D,0x45,0x47,0x41,0x20,0x44,0x52,0x49,0x56,0x45,0x20
};
private static final int[] sega_Genesis____ = {
0x53,0x45,0x47,0x41,0x20,0x47,0x45,0x4E,0x45,0x53,0x49,0x53,0x20,0x20,0x20,0x20
} ;
private static final int[] sega_32X________ = {
0x53,0x45,0x47,0x41,0x20,0x33,0x32,0x58,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20
} ;
private static final int[][] console_IDs = {
sega_Mega_Drive_,
sega_Genesis____,
sega_32X________
};
/**
*
* Detect if this is a Sega Genesis / Master System / 32X file.
*
**/
public boolean doFormatDetect(final byte[] data, String extension) {
// verify extension
if (!(extension.equals("smd")
|| extension.equals("md"))) {
return false;
}
if (extension.equals("smd")) {
int[] offsets = {
0x2280, 0x0280, 0x2281, 0x0281, 0x2282, 0x0282, 0x2283, 0x0283,
0x2284, 0x0284, 0x2285, 0x0285, 0x2286, 0x0286, 0x2287, 0x0287
};
boolean id_OK = false;
for (int i=0; i<console_IDs.length; i++) {
int[] id_String = console_IDs[i];
int diff = 0;
for (int j=0; j<id_String.length; j++) {
diff += Math.abs((data[offsets[j]] & 0xFF) - id_String[j]);
}
if (diff == 0) {
id_OK = true;
break;
}
}
if (!id_OK) {
return false;
}
}
else {
// md extension
}
// verify file size
if (((data.length-512) % 16384) != 0) {
return false;
}
// (data[8] & 0xFF) == 0xAA
// (data[9] & 0xFF) == 0xBB
// (data[10] & 0xFF) == 0x06
return true;
}
/**
*
* Deinterleaves the data on file load.
*
**/
public void fileLoaded(byte[] data, String extension) {
if (extension.equals("smd")) {
byte[] block = new byte[16384];
int blockCount = (data.length-512) / 16384;
for (int i=0; i<blockCount; i++) {
// decode a block
int ofs = (i * 16384) + 512;
// odd - 0..8191
for (int j=1; j<16384; j+=2) {
block[j] = data[ofs++];
}
// even - 8192..16383
for (int j=0; j<16384; j+=2) {
block[j] = data[ofs++];
}
// store the decoded block
ofs = (i * 16384) + 512;
for (int j=0; j<16384; j++) {
data[ofs+j] = block[j];
}
}
}
else {
// md extension
}
}
/**
*
* Reinterleaves the data on file save.
*
**/
public void fileSaving(byte[] data, String extension) {
if (extension.equals("smd")) {
// update checksum
int checkSum = 0;
for (int i=1024; i<data.length; i+=2) {
checkSum += (data[i] & 0xFF) << 8;
checkSum += data[i+1] & 0xFF;
}
data[0x38E] = (byte)((checkSum >> 8) & 0xFF);
data[0x38F] = (byte)(checkSum & 0xFF);
// reinterleave data
byte[] block = new byte[16384];
int blockCount = (data.length-512) / 16384;
for (int i=0; i<blockCount; i++) {
// encode a block
// odd - 0..8191
int ofs = (i * 16384) + 512 + 1;
for (int j=0; j<8192; j++) {
block[j] = data[ofs];
ofs += 2;
}
// even - 8192..16383
ofs = (i * 16384) + 512;
for (int j=8192; j<16384; j++) {
block[j] = data[ofs];
ofs += 2;
}
// store the encoded block
ofs = (i * 16384) + 512;
for (int j=0; j<16384; j++) {
data[ofs+j] = block[j];
}
}
}
else {
// md extension
}
}
}

View file

@ -0,0 +1,84 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.filelistener;
/**
*
* Listener for Sega Master System (*.sms) files.
*
**/
public class SegaMasterSystemFileListener extends TMFileListener {
private static final int[] tmr_Sega = { 0x54,0x4D,0x52,0x20,0x53,0x45,0x47,0x41 };
/**
*
* Detects if this is a Sega Master System file.
* The criteria are that the extension is either sms or gg and that the string
* "TMR SEGA" appears at offset 0x7FF0.
*
**/
public boolean doFormatDetect(final byte[] data, String extension) {
// verify extension
if (!(extension.equals("sms")
|| extension.equals("gg"))) {
return false;
}
// verify "TMR SEGA" string
for (int i=0; i<tmr_Sega.length; i++) {
if ((data[0x7FF0+i] & 0xFF) != tmr_Sega[i]) {
return false;
}
}
return true;
}
/**
*
* Does nothing on file load.
*
**/
public void fileLoaded(byte[] data, String extension) {
}
/**
*
* Recalculates the checksum on file save.
*
**/
public void fileSaving(byte[] data, String extension) {
// update checksum
int checkSum = 0;
for (int i=0; i<0x7FF0; i++) {
checkSum += data[i] & 0xFF;
}
for (int i=0x8000; i<data.length; i++) {
checkSum += data[i] & 0xFF;
}
data[0x7FFA] = (byte)(checkSum & 0xFF);
data[0x7FFB] = (byte)((checkSum >> 8) & 0xFF);
}
}

View file

@ -0,0 +1,64 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.filelistener;
/**
*
* Abstract class that defines the interface for filelisteners.
* A <code>filelistener</code> is an object that is notified when a file has
* been fully loaded into memory, as well as when it is about to be saved.
* At these times it may perform various operations on the data being saved,
* such as repairing checksums.
* The fileformatlistener also has to implement a method that determines if
* the file being saved is indeed of a supported format. This usually involves
* checking the header (verifying ID strings and such).
*
**/
public abstract class TMFileListener {
/**
*
* This method is invoked when a file has been loaded, to give the file
* listener a chance to detect the file format. If it returns <code>true</code>,
* this file listener will receive all subsequent fileLoaded() and fileSaved()
* events for the file.
*
**/
public abstract boolean doFormatDetect(final byte[] data, String extension);
/**
*
* This method is invoked when the file has been loaded (and doFormatDetect() has
* already returned true.)
*
**/
public abstract void fileLoaded(byte[] data, String extension);
/**
*
* This method is invoked when the file is about to be saved.
*
**/
public abstract void fileSaving(byte[] data, String extension);
}

View file

@ -0,0 +1,72 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.fileselection;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import javax.swing.JOptionPane;
/**
*
* A filechooser that verifies that the selected file exists and
* that it is readable before it lets the user proceed.
*
**/
public class TMApprovedFileOpenChooser extends JFileChooser {
public void approveSelection() {
File file = getSelectedFile();
// verify that it has proper extension
FileFilter ff = getFileFilter();
if (!ff.accept(file)) {
String absPath = file.getAbsolutePath();
if (!absPath.endsWith(".")) absPath += ".";
String[] exts = ((TMFileFilter)ff).getExtensions();
File tempFile=null;
for (int i=0; i<exts.length; i++) {
String tempPath = absPath + exts[i];
tempFile = new File(tempPath);
if (tempFile.exists()) {
setSelectedFile(tempFile);
break;
}
}
}
// verify that file exists and can be read
file = getSelectedFile();
if (!file.exists()) {
JOptionPane.showMessageDialog(getParent(),
"The file '"+file.getName()+"' does not exist.", // i18n
"",
JOptionPane.ERROR_MESSAGE);
}
else if (!file.canRead()) {
JOptionPane.showMessageDialog(getParent(),
"The file '"+file.getName()+"' cannot be read.\nIt may be in use by another application.", // i18n
"",
JOptionPane.ERROR_MESSAGE);
}
else {
super.approveSelection();
}
}
}

View file

@ -0,0 +1,79 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.fileselection;
import java.io.File;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import javax.swing.JOptionPane;
/**
*
* A filechooser that verifies that the selected file doesn't exist;
* if it does, the user is asked whether he wants to overwrite it
* before he can proceed.
*
**/
public class TMApprovedFileSaveChooser extends JFileChooser {
public void approveSelection() {
File file = getSelectedFile();
// verify that it has proper extension
FileFilter ff = getFileFilter();
if (!ff.accept(file)) {
String absPath = file.getAbsolutePath();
if (!absPath.endsWith(".")) absPath += ".";
setSelectedFile(new File(absPath+((TMFileFilter)ff).getDefaultExtension()));
}
file = getSelectedFile();
if (file.exists()) {
int retVal = JOptionPane.showConfirmDialog(getParent(),
"The file '"+file.getName()+"' already exists.\nDo you wish to overwrite it?", // i18n
"",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (retVal == JOptionPane.NO_OPTION)
return;
// delete and recreate the file
file.delete();
try {
file.createNewFile();
}
catch (Exception e) {
e.printStackTrace();
}
}
else {
try {
file.createNewFile();
}
catch (Exception e) {
e.printStackTrace();
}
}
if (!file.canWrite()) {
JOptionPane.showMessageDialog(getParent(),
"The file '"+file.getName()+"' cannot be written to.", // i18n
"",
JOptionPane.ERROR_MESSAGE);
}
super.approveSelection();
}
}

View file

@ -0,0 +1,169 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.fileselection;
import javax.swing.filechooser.FileFilter;
import java.io.File;
import java.util.StringTokenizer;
/**
*
* A file filter that allows you to register a set of extensions with a common
* description.
*
**/
public class TMFileFilter extends FileFilter {
private String description;
private String[] extensions;
private String extlist;
public TMFileFilter() {
super();
}
/**
*
* @param extlist A comma-separated list of extensions
*
**/
public TMFileFilter(String extlist, String description) {
super();
setExtensions(extlist);
setDescription(description);
}
/**
*
*
*
**/
public void setExtensions(String extlist) {
this.extlist = extlist;
StringTokenizer st = new StringTokenizer(extlist, ",");
extensions = new String[st.countTokens()];
for (int i=0; i<extensions.length; i++) {
extensions[i] = st.nextToken();
}
}
/**
*
*
*
**/
public void setDescription(String description) {
this.description = description;
}
/**
*
*
*
**/
public String[] getExtensions() {
return extensions;
}
/**
*
*
*
**/
public String getExtlist() {
return extlist;
}
/**
*
*
*
**/
public String getDefaultExtension() {
return extensions[0];
}
/**
*
*
*
**/
public String getDescription() {
return description;
}
/**
*
*
*
**/
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String ext = getExtension(f);
for (int i=0; i<extensions.length; i++) {
String suppExt = extensions[i];
if (suppExt.length() != ext.length()) {
continue; // can't possibly match if length different
}
boolean match = true;
for (int j=0; j<ext.length(); j++) {
if (suppExt.charAt(j) == '?') {
continue; // '?' matches any character
}
if (suppExt.charAt(j) != ext.charAt(j)) {
match = false;
break;
}
}
if (match) {
return true;
}
}
return false;
}
/**
*
* Gets the file extension.
*
**/
public static String getExtension(File f) {
String ext = "";
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i+1).toLowerCase();
}
return ext;
}
}

View file

@ -0,0 +1,88 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.fileselection;
/**
*
*
*
**/
public class TMPaletteFileFilter extends TMFileFilter {
private String codecID; // color codec
private int size;
private int offset;
private int endianness;
/**
*
*
*
**/
public TMPaletteFileFilter(String extlist, String description, String codecID, int size, int offset, int endianness) {
super(extlist, description);
this.codecID = codecID;
this.size = size;
this.offset = offset;
this.endianness = endianness;
}
/**
*
*
*
**/
public String getCodecID() {
return codecID;
}
/**
*
*
*
**/
public int getSize() {
return size;
}
/**
*
*
*
**/
public int getOffset() {
return offset;
}
/**
*
*
*
**/
public int getEndianness() {
return endianness;
}
}

View file

@ -0,0 +1,78 @@
/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Molester.
*
* Tile Molester is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Tile Molester is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
package tm.fileselection;
public class TMTileCodecFileFilter extends TMFileFilter {
private int defaultMode;
private String codecID;
/**
*
*
*
**/
public TMTileCodecFileFilter(String extlist, String description, String codecID, int defaultMode) {
super(extlist, description);
setCodecID(codecID);
setDefaultMode(defaultMode);
}
/**
*
*
*
**/
public void setCodecID(String codecID) {
this.codecID = codecID;
}
/**
*
*
*
**/
public void setDefaultMode(int defaultMode) {
this.defaultMode = defaultMode;
}
/**
*
*
*
**/
public int getDefaultMode() {
return defaultMode;
}
/**
*
*
*
**/
public String getCodecID() {
return codecID;
}
}

View file

@ -0,0 +1,543 @@
//
// This code was taken and cleaned up from a
// Javaworld tips and tricks column
//
package tm.gfxlibs;
import java.awt.image.*;
import java.awt.*;
import java.io.*;
import javax.swing.*;
//
// really just a collection of methods to read a BMP file
//
public class BMPLoader
{
// build an int from a byte array - convert little to big endian
public static int constructInt(byte[] in,int offset) {
int ret = ((int)in[offset + 3] & 0xff);
ret = (ret << 8) | ((int)in[offset + 2] & 0xff);
ret = (ret << 8) | ((int)in[offset + 1] & 0xff);
ret = (ret << 8) | ((int)in[offset + 0] & 0xff);
return(ret);
}
// build an int from a byte array - convert little to big endian
// set high order bytes to 0xfff
public static int constructInt3(byte[] in,int offset) {
int ret = 0xff;
ret = (ret << 8) | ((int)in[offset + 2] & 0xff);
ret = (ret << 8) | ((int)in[offset + 1] & 0xff);
ret = (ret << 8) | ((int)in[offset + 0] & 0xff);
return(ret);
}
// build an int from a byte array - convert little to big endian
public static long constructLong(byte[] in,int offset) {
long ret = ((long)in[offset + 7] & 0xff);
ret |= (ret << 8) | ((long)in[offset + 6] & 0xff);
ret |= (ret << 8) | ((long)in[offset + 5] & 0xff);
ret |= (ret << 8) | ((long)in[offset + 4] & 0xff);
ret |= (ret << 8) | ((long)in[offset + 3] & 0xff);
ret |= (ret << 8) | ((long)in[offset + 2] & 0xff);
ret |= (ret << 8) | ((long)in[offset + 1] & 0xff);
ret |= (ret << 8) | ((long)in[offset + 0] & 0xff);
return(ret);
}
// build an double from a byte array - convert little to big endian
public static double constructDouble(byte[] in,int offset) {
long ret = constructLong(in,offset);
return(Double.longBitsToDouble(ret));
}
// build an short from a byte array - convert little to big endian
public static short constructShort(byte[] in,int offset) {
short ret = (short)(in[(offset + 1)] & 0xFF);
ret = (short)(ret << 8 | (short)(in[(offset + 0)] & 0xFF));
return ret;
}
//
// internal class representing a bitmap header structure
// with code to read it from a file
static class BitmapHeader {
public int nsize;
public int nbisize;
public int nwidth;
public int nheight;
public int nplanes;
public int nbitcount;
public int ncompression;
public int nsizeimage;
public int nxpm;
public int nypm;
public int nclrused;
public int nclrimp;
// read in the bitmap header
public void read(FileInputStream fs) throws IOException
{
final int bflen=14; // 14 byte BITMAPFILEHEADER
byte bf[]=new byte[bflen];
fs.read(bf,0,bflen);
final int bilen=40; // 40-byte BITMAPINFOHEADER
byte bi[]=new byte[bilen];
fs.read(bi,0,bilen);
// Interperet data.
nsize = constructInt(bf,2);
// System.out.println("File type is :"+(char)bf[0]+(char)bf[1]);
// System.out.println("Size of file is :"+nsize);
nbisize = constructInt(bi,2);
// System.out.println("Size of bitmapinfoheader is :"+nbisize);
nwidth = constructInt(bi,4);
// System.out.println("Width is :"+nwidth);
nheight = constructInt(bi,8);
// System.out.println("Height is :"+nheight);
nplanes = constructShort(bi,12); //(((int)bi[13]&0xff)<<8) | (int)bi[12]&0xff;
// System.out.println("Planes is :"+nplanes);
nbitcount = constructShort(bi,14); //(((int)bi[15]&0xff)<<8) | (int)bi[14]&0xff;
// System.out.println("BitCount is :"+nbitcount);
// Look for non-zero values to indicate compression
ncompression = constructInt(bi,16);
// System.out.println("Compression is :"+ncompression);
nsizeimage = constructInt(bi,20);
// System.out.println("SizeImage is :"+nsizeimage);
nxpm = constructInt(bi,24);
// System.out.println("X-Pixels per meter is :"+nxpm);
nypm = constructInt(bi,28);
// System.out.println("Y-Pixels per meter is :"+nypm);
nclrused = constructInt(bi,32);
// System.out.println("Colors used are :"+nclrused);
nclrimp = constructInt(bi,36);
// System.out.println("Colors important are :"+nclrimp);
}
}
public static Image read(FileInputStream fs)
{
try {
BitmapHeader bh = new BitmapHeader();
bh.read(fs);
if (bh.nbitcount==24)
return(readMap24(fs,bh));
if (bh.nbitcount==32)
return(readMap32(fs,bh));
if (bh.nbitcount==8)
return(readMap8(fs,bh));
fs.close();
}
catch (IOException e)
{
// System.out.println("Caught exception in loadbitmap!");
}
return(null);
}
/**
readMap24 internal routine to read the bytes in a 24 bit bitmap
Arguments:
fs - file stream
bh - header struct
Returns:
Image Object, be sure to check for (Image)null !!!!
*/
protected static Image readMap32(FileInputStream fs,BitmapHeader bh) throws IOException
{
Image image;
// No Palatte data for 24-bit format but scan lines are
// padded out to even 4-byte boundaries.
int xwidth = bh.nsizeimage / bh.nheight;
int ndata[] = new int [bh.nheight * bh.nwidth];
byte brgb[] = new byte [ bh.nwidth * 4 * bh.nheight];
fs.read (brgb, 0, bh.nwidth * 4 * bh.nheight);
int nindex = 0;
for (int j = 0; j < bh.nheight; j++)
{
for (int i = 0; i < bh.nwidth; i++)
{
ndata [bh.nwidth * (bh.nheight - j - 1) + i] = constructInt3(brgb,nindex);
nindex += 4;
}
}
image = Toolkit.getDefaultToolkit().createImage
( new MemoryImageSource(bh.nwidth, bh.nheight,
ndata, 0, bh.nwidth));
fs.close();
return(image);
}
/**
readMap24 internal routine to read the bytes in a 24 bit bitmap
Arguments:
fs - file stream
bh - header struct
Returns:
Image Object, be sure to check for (Image)null !!!!
*/
protected static Image readMap24(FileInputStream fs,BitmapHeader bh) throws IOException
{
Image image;
// No Palatte data for 24-bit format but scan lines are
// padded out to even 4-byte boundaries.
int npad = (bh.nsizeimage / bh.nheight) - bh.nwidth * 3;
int ndata[] = new int [bh.nheight * bh.nwidth];
byte brgb[] = new byte [( bh.nwidth + npad) * 3 * bh.nheight];
fs.read (brgb, 0, (bh.nwidth + npad) * 3 * bh.nheight);
int nindex = 0;
for (int j = 0; j < bh.nheight; j++)
{
for (int i = 0; i < bh.nwidth; i++)
{
ndata [bh.nwidth * (bh.nheight - j - 1) + i] = constructInt3(brgb,nindex);
nindex += 3;
}
nindex += npad;
}
image = Toolkit.getDefaultToolkit().createImage
( new MemoryImageSource(bh.nwidth, bh.nheight,
ndata, 0, bh.nwidth));
fs.close();
return(image);
}
/**
readMap8 internal routine to read the bytes in a 8 bit bitmap
Arguments:
fs - file stream
bh - header struct
Returns:
Image Object, be sure to check for (Image)null !!!!
*/
protected static Image readMap8(FileInputStream fs,BitmapHeader bh) throws IOException
{
Image image;
// Have to determine the number of colors, the clrsused
// parameter is dominant if it is greater than zero. If
// zero, calculate colors based on bitsperpixel.
int nNumColors = 0;
if (bh.nclrused > 0)
{
nNumColors = bh.nclrused;
}
else
{
nNumColors = (1&0xff)<< bh.nbitcount;
}
// System.out.println("The number of Colors is"+nNumColors);
// Some bitmaps do not have the sizeimage field calculated
// Ferret out these cases and fix 'em.
if (bh.nsizeimage == 0)
{
bh.nsizeimage = ((((bh.nwidth* bh.nbitcount)+31) & ~31 ) >> 3);
bh.nsizeimage *= bh.nheight;
// System.out.println("nsizeimage (backup) is"+nsizeimage);
}
// Read the palatte colors.
int npalette[] = new int [nNumColors];
byte bpalette[] = new byte [nNumColors*4];
fs.read (bpalette, 0, nNumColors*4);
int nindex8 = 0;
for (int n = 0; n < nNumColors; n++)
{
npalette[n] = constructInt3(bpalette,nindex8);
nindex8 += 4;
}
// Read the image data (actually indices into the palette)
// Scan lines are still padded out to even 4-byte
// boundaries.
int npad8 = (bh.nsizeimage / bh.nheight) - bh.nwidth;
// System.out.println("nPad is:"+npad8);
int ndata8[] = new int [bh.nwidth * bh.nheight];
byte bdata[] = new byte [(bh.nwidth+npad8)* bh.nheight];
fs.read (bdata, 0, (bh.nwidth+npad8)*bh.nheight);
nindex8 = 0;
for (int j8 = 0; j8 < bh.nheight; j8++)
{
for (int i8 = 0; i8 < bh.nwidth; i8++)
{
ndata8[(bh.nwidth * (bh.nheight - j8 - 1) + i8)] =
npalette[(bdata[nindex8] & 0xFF)];
nindex8++;
}
nindex8 += npad8;
}
image = Toolkit.getDefaultToolkit().createImage
( new MemoryImageSource (bh.nwidth, bh.nheight,
ndata8, 0, bh.nwidth));
return(image);
}
/**
load method - see read for details
Arguments:
sdir and sfile are the result of the FileDialog()
getDirectory() and getFile() methods.
Returns:
Image Object, be sure to check for (Image)null !!!!
*/
public static Image load(String sdir, String sfile) {
return(load(sdir + sfile));
}
/**
load method - see read for details
Arguments:
sdir - full path name
Returns:
Image Object, be sure to check for (Image)null !!!!
*/
public static Image load(String sdir)
{
try
{
FileInputStream fs=new FileInputStream(sdir);
return(read(fs));
}
catch(IOException ex) {
return(null);
}
}
public static void main(String[] args) throws IOException
{
if(args.length == 0){
System.out.println("Usage >java BMPLoader ImageFile.bmp");
System.exit(0);
}
FileInputStream in = new FileInputStream(args[0]);
Image TheImage = read(in);
JFrame TheFrame = new JFrame(args[0]);
JLabel TheLabel = new JLabel(new ImageIcon(TheImage));
TheFrame.getContentPane().add(new JScrollPane(TheLabel));
TheFrame.setSize(300,300);
TheFrame.setVisible(true);
}
// end class BMPLoader
}

View file

@ -0,0 +1,305 @@
package tm.gfxlibs;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
// This class provides a public static method that takes an InputStream
// to a Windows .BMP file and converts it into an ImageProducer via
// a MemoryImageSource.
// You can fetch a .BMP through a URL with the following code:
// URL url = new URL( <wherever your URL is> )
// Image img = createImage(BMPReader.getBMPImage(url.openStream()));
public class BMPReader extends Object
{
// Constants indicating how the data is stored
public static final int BI_RGB = 0;
public static final int BI_RLE8 = 1;
public static final int BI_RLE4 = 2;
public static ImageProducer getBMPImage(InputStream stream)
throws IOException
{
// The DataInputStream allows you to read in 16 and 32 bit numbers
DataInputStream in = new DataInputStream(stream);
// Verify that the header starts with 'BM'
if (in.read() != 'B') {
throw new IOException("Not a .BMP file");
}
if (in.read() != 'M') {
throw new IOException("Not a .BMP file");
}
// Get the total file size
int fileSize = intelInt(in.readInt());
// Skip the 2 16-bit reserved words
in.readUnsignedShort();
in.readUnsignedShort();
int bitmapOffset = intelInt(in.readInt());
int bitmapInfoSize = intelInt(in.readInt());
int width = intelInt(in.readInt());
int height = intelInt(in.readInt());
// Skip the 16-bit bitplane size
in.readUnsignedShort();
int bitCount = intelShort(in.readUnsignedShort());
int compressionType = intelInt(in.readInt());
int imageSize = intelInt(in.readInt());
// Skip pixels per meter
in.readInt();
in.readInt();
int colorsUsed = intelInt(in.readInt());
int colorsImportant = intelInt(in.readInt());
if (colorsUsed == 0) colorsUsed = 1 << bitCount;
int colorTable[] = new int[colorsUsed];
// Read the bitmap's color table
for (int i=0; i < colorsUsed; i++) {
colorTable[i] = (intelInt(in.readInt()) & 0xffffff) + 0xff000000;
}
// Create space for the pixels
int pixels[] = new int[width * height];
// Read the pixels from the stream based on the compression type
if (compressionType == BI_RGB) {
if (bitCount == 24) {
readRGB24(width, height, pixels, in);
} else {
readRGB(width, height, colorTable, bitCount,
pixels, in);
}
} else if (compressionType == BI_RLE8) {
readRLE(width, height, colorTable, bitCount,
pixels, in, imageSize, 8);
} else if (compressionType == BI_RLE4) {
readRLE(width, height, colorTable, bitCount,
pixels, in, imageSize, 4);
}
// Create a memory image source from the pixels
return new MemoryImageSource(width, height, pixels, 0,
width);
}
// Reads in pixels in 24-bit format. There is no color table, and the
// pixels are stored in 3-byte pairs. Oddly, all windows bitmaps are
// stored upside-down - the bottom line is stored first.
protected static void readRGB24(int width, int height, int pixels[],
DataInputStream in)
throws IOException
{
// Start storing at the bottom of the array
for (int h = height-1; h >= 0; h--) {
int pos = h * width;
for (int w = 0; w < width; w++) {
// Read in the red, green, and blue components
int red = in.read();
int green = in.read();
int blue = in.read();
// Turn the red, green, and blue values into an RGB color with
// an alpha value of 255 (fully opaque)
pixels[pos++] = 0xff000000 + (red << 16) +
(green << 8) + blue;
}
}
}
// readRGB reads in pixels values that are stored uncompressed.
// The bits represent indices into the color table.
protected static void readRGB(int width, int height, int colorTable[],
int bitCount, int pixels[], DataInputStream in)
throws IOException
{
// How many pixels can be stored in a byte?
int pixelsPerByte = 8 / bitCount;
// A bit mask containing the number of bits in a pixel
int bitMask = (1 << bitCount) - 1;
// The shift values that will move each pixel to the far right
int bitShifts[] = new int[pixelsPerByte];
for (int i=0; i < pixelsPerByte; i++) {
bitShifts[i] = 8 - ((i+1) * bitCount);
}
int whichBit = 0;
// Read in the first byte
int currByte = in.read();
// Start at the bottom of the pixel array and work up
for (int h=height-1; h >= 0; h--) {
int pos = h * width;
for (int w=0; w < width; w++) {
// Get the next pixel from the current byte
pixels[pos] = colorTable[
(currByte >> bitShifts[whichBit]) &
bitMask];
pos++;
whichBit++;
// If the current bit position is past the number of pixels in
// a byte, we advance to the next byte
if (whichBit >= pixelsPerByte) {
whichBit = 0;
currByte = in.read();
}
}
}
}
// readRLE reads run-length encoded data in either RLE4 or RLE8 format.
protected static void readRLE(int width, int height, int colorTable[],
int bitCount, int pixels[], DataInputStream in,
int imageSize, int pixelSize)
throws IOException
{
int x = 0;
int y = height-1;
// You already know how many bytes are in the image, so only go
// through that many.
for (int i=0; i < imageSize; i++) {
// RLE encoding is defined by two bytes
int byte1 = in.read();
int byte2 = in.read();
i += 2;
// If byte 0 == 0, this is an escape code
if (byte1 == 0) {
// If escaped, byte 2 == 0 means you are at end of line
if (byte2 == 0) {
x = 0;
y--;
// If escaped, byte 2 == 1 means end of bitmap
} else if (byte2 == 1) {
return;
// if escaped, byte 2 == 2 adjusts the current x and y by
// an offset stored in the next two words
} else if (byte2 == 2) {
int xoff = (char) intelShort(
in.readUnsignedShort());
i+= 2;
int yoff = (char) intelShort(
in.readUnsignedShort());
i+= 2;
x += xoff;
y -= yoff;
// If escaped, any other value for byte 2 is the number of bytes
// that you should read as pixel values (these pixels are not
// run-length encoded)
} else {
int whichBit = 0;
// Read in the next byte
int currByte = in.read();
i++;
for (int j=0; j < byte2; j++) {
if (pixelSize == 4) {
// The pixels are 4-bits, so half the time you shift the current byte
// to the right as the pixel value
if (whichBit == 0) {
pixels[y*width+x] = colorTable[(currByte >> 4)
& 0xf];
} else {
// The rest of the time, you mask out the upper 4 bits, save the pixel
// value, then read in the next byte
pixels[y*width+x] = colorTable[currByte & 0xf];
currByte = in.read();
i++;
}
} else {
pixels[y*width+x] = colorTable[currByte];
currByte = in.read();
i++;
}
x++;
if (x >= width) {
x = 0;
y--;
}
}
// The pixels must be word-aligned, so if you read an uneven number of
// bytes, read and ignore a byte to get aligned again.
if ((byte2 & 1) == 1) {
in.read();
i++;
}
}
// If the first byte was not 0, it is the number of pixels that
// are encoded by byte 2
} else {
for (int j=0; j < byte1; j++) {
if (pixelSize == 4) {
// If j is odd, use the upper 4 bits
if ((j & 1) == 0) {
pixels[y*width+x] = colorTable[(byte2 >> 4) & 0xf];
} else {
pixels[y*width+x+1] = colorTable[byte2 & 0xf];
}
} else {
pixels[y*width+x+1] = colorTable[byte2];
}
x++;
if (x >= width) {
x = 0;
y--;
}
}
}
}
}
// intelShort converts a 16-bit number stored in intel byte order into
// the local host format
protected static int intelShort(int i)
{
return ((i >> 8) & 0xff) + ((i << 8) & 0xff00);
}
// intelInt converts a 32-bit number stored in intel byte order into
// the local host format
protected static int intelInt(int i)
{
return ((i & 0xff) << 24) + ((i & 0xff00) << 8) +
((i & 0xff0000) >> 8) + ((i >> 24) & 0xff);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,271 @@
// ImageEncoder - abstract class for writing out an image
//
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/
package tm.gfxlibs;
import java.util.*;
import java.io.*;
import java.awt.Image;
import java.awt.image.*;
/// Abstract class for writing out an image.
// <P>
// A framework for classes that encode and write out an image in
// a particular file format.
// <P>
// This provides a simplified rendition of the ImageConsumer interface.
// It always delivers the pixels as ints in the RGBdefault color model.
// It always provides them in top-down left-right order.
// If you want more flexibility you can always implement ImageConsumer
// directly.
// <P>
// <A HREF="/resources/classes/Acme/JPM/Encoders/ImageEncoder.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
// <P>
// @see GifEncoder
// @see PpmEncoder
// @see Acme.JPM.Decoders.ImageDecoder
public abstract class ImageEncoder implements ImageConsumer
{
protected OutputStream out;
private ImageProducer producer;
private int width = -1;
private int height = -1;
private int hintflags = 0;
private boolean started = false;
private boolean encoding;
private IOException iox;
private static final ColorModel rgbModel = ColorModel.getRGBdefault();
private Hashtable props = null;
/// Constructor.
// @param img The image to encode.
// @param out The stream to write the bytes to.
public ImageEncoder( Image img, OutputStream out ) throws IOException
{
this( img.getSource(), out );
}
/// Constructor.
// @param producer The ImageProducer to encode.
// @param out The stream to write the bytes to.
public ImageEncoder( ImageProducer producer, OutputStream out ) throws IOException
{
this.producer = producer;
this.out = out;
}
// Methods that subclasses implement.
/// Subclasses implement this to initialize an encoding.
abstract void encodeStart( int w, int h ) throws IOException;
/// Subclasses implement this to actually write out some bits. They
// are guaranteed to be delivered in top-down-left-right order.
// One int per pixel, index is row * scansize + off + col,
// RGBdefault (AARRGGBB) color model.
abstract void encodePixels(
int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
throws IOException;
/// Subclasses implement this to finish an encoding.
abstract void encodeDone() throws IOException;
// Our own methods.
/// Call this after initialization to get things going.
public synchronized void encode() throws IOException
{
encoding = true;
iox = null;
producer.startProduction( this );
while ( encoding )
try
{
wait();
}
catch ( InterruptedException e ) {}
if ( iox != null )
throw iox;
}
private boolean accumulate = false;
private int[] accumulator;
private void encodePixelsWrapper(
int x, int y, int w, int h, int[] rgbPixels, int off, int scansize )
throws IOException
{
if ( ! started )
{
started = true;
encodeStart( width, height );
if ( ( hintflags & TOPDOWNLEFTRIGHT ) == 0 )
{
accumulate = true;
accumulator = new int[width * height];
}
}
if ( accumulate )
for ( int row = 0; row < h; ++row )
System.arraycopy(
rgbPixels, row * scansize + off,
accumulator, ( y + row ) * width + x,
w );
else
encodePixels( x, y, w, h, rgbPixels, off, scansize );
}
private void encodeFinish() throws IOException
{
if ( accumulate )
{
encodePixels( 0, 0, width, height, accumulator, 0, width );
accumulator = null;
accumulate = false;
}
}
private synchronized void stop()
{
encoding = false;
notifyAll();
}
// Methods from ImageConsumer.
public void setDimensions( int width, int height )
{
this.width = width;
this.height = height;
}
public void setProperties( Hashtable props )
{
this.props = props;
}
public void setColorModel( ColorModel model )
{
// Ignore.
}
public void setHints( int hintflags )
{
this.hintflags = hintflags;
}
public void setPixels(
int x, int y, int w, int h, ColorModel model, byte[] pixels,
int off, int scansize )
{
int[] rgbPixels = new int[w];
for ( int row = 0; row < h; ++row )
{
int rowOff = off + row * scansize;
for ( int col = 0; col < w; ++col )
rgbPixels[col] = model.getRGB( pixels[rowOff + col] & 0xff );
try
{
encodePixelsWrapper( x, y + row, w, 1, rgbPixels, 0, w );
}
catch ( IOException e )
{
iox = e;
stop();
return;
}
}
}
public void setPixels(
int x, int y, int w, int h, ColorModel model, int[] pixels,
int off, int scansize )
{
if ( model == rgbModel )
{
try
{
encodePixelsWrapper( x, y, w, h, pixels, off, scansize );
}
catch ( IOException e )
{
iox = e;
stop();
return;
}
}
else
{
int[] rgbPixels = new int[w];
for ( int row = 0; row < h; ++row )
{
int rowOff = off + row * scansize;
for ( int col = 0; col < w; ++col )
rgbPixels[col] = model.getRGB( pixels[rowOff + col] );
try
{
encodePixelsWrapper( x, y + row, w, 1, rgbPixels, 0, w );
}
catch ( IOException e )
{
iox = e;
stop();
return;
}
}
}
}
public void imageComplete( int status )
{
producer.removeConsumer( this );
if ( status == ImageConsumer.IMAGEABORTED )
iox = new IOException( "image aborted" );
else
{
try
{
encodeFinish();
encodeDone();
}
catch ( IOException e )
{
iox = e;
}
}
stop();
}
}

View file

@ -0,0 +1,147 @@
/**
*
* @author Kent Hansen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
**/
package tm.gfxlibs;
import java.awt.Image;
import java.awt.image.PixelGrabber;
import java.awt.image.ImageObserver;
import java.io.ByteArrayOutputStream;
public class PCXEncoder {
private static ByteArrayOutputStream baos = new ByteArrayOutputStream();
public static byte[] encode(Image image) {
// get width and height of image
int w = image.getWidth(null);
int h = image.getHeight(null);
// grab the pixels
int[] pixels = new int[w * h];
PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
System.err.println("interrupted waiting for pixels!");
return null;
}
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
return null;
}
// prepare the byte stream
baos.reset();
// write the header
emitByte(10); // Manufacturer = ZSoft
emitByte(5); // Version = 3.0+ (supports 24-bit PCX)
emitByte(1); // Encoding = RLE
emitByte(8); // BitsPerPixel (per plane)
emitInt(0); // Xmin
emitInt(0); // Ymin
emitInt(w-1); // Xmax
emitInt(h-1); // Ymax
emitInt(0); // HDpi (not used)
emitInt(0); // VDpi (not used)
emitZeroes(48); // Colormap (not used)
emitZeroes(1); // Reserved
emitByte(3); // NPlanes
emitInt(w); // BytesPerLine
emitInt(0); // PaletteInfo (not used)
emitInt(w); // HScreenSize (unsure what this is for)
emitInt(h); // VScreenSize (unsure what this is for)
emitZeroes(54); // Filler
// encode the pixels
int ofs=0;
int[] plane = new int[w];
for (int i=0; i<h; i++) {
// red plane
for (int j=0; j<w; j++) {
plane[j] = getRed(pixels[ofs+j]);
}
encodePlane(plane);
// green plane
for (int j=0; j<w; j++) {
plane[j] = getGreen(pixels[ofs+j]);
}
encodePlane(plane);
// blue plane
for (int j=0; j<w; j++) {
plane[j] = getBlue(pixels[ofs+j]);
}
encodePlane(plane);
ofs += w;
}
return baos.toByteArray();
}
private static void emitByte(int b) {
baos.write(b);
}
private static void emitInt(int i) {
baos.write(i & 0xFF);
baos.write((i >> 8) & 0xFF);
}
private static void emitZeroes(int c) {
for (int i=0; i<c; i++) {
emitByte(0x00);
}
}
private static int getRed(int rgb) {
return (rgb >> 16) & 0xFF;
}
private static int getGreen(int rgb) {
return (rgb >> 8) & 0xFF;
}
private static int getBlue(int rgb) {
return rgb & 0xFF;
}
private static void encodePlane(int[] plane) {
int i=0;
while (i < plane.length) {
int v = plane[i];
if ((i != plane.length-1) && (v == plane[i+1])) {
// run-length encode
int c = 1;
while ((c < 0x40) && (i+c < plane.length) && (v == plane[i+c])) {
c++;
}
i += c;
emitByte(c | 0xC0);
emitByte(v);
}
else {
// regular encode
if ((v & 0xC0) == 0xC0) {
emitByte(0xC1);
}
emitByte(v);
i++;
}
}
}
}

View file

@ -0,0 +1,236 @@
/* PcxReader: This is a class which provides a method for reading
* PCX-Files.
* The PCX-Format is a Image-File-Format which was developed by ZSoft.
*
* PcxReader, Version 1.00 [06/05/2000]
* Copyright (c) 2000 by Matthias Burg
* All rights reserved
* eMail: Matthias@burgsoft.de
* Internet: www.burgsoft.de
*
* The PcxReader is Freeware. You can use and copy it without any fee.
* The author is not responsible for any damages which are caused by
* this software.
* You find further information about the Java-Technology on the Sun-
* Website (Internet: www.sun.com).
*
* At the moment PcxReader supports the following File-Formats:
* - PCX Version 3.0 with 8 Bit (=256) Colors
* - PCX Version 3.0 with 24 Bit (=16.7 Mio) Colors
*
* The PcxReader needs an opened InputStream with the PCX-Data as
* Argument. The return-value is the loaded Image.
* You can use the PcxReader in a Java-Application as well as in a
* Java-Applet
*
* If you have questions or tips for the PcxReader, please write an
* eMail.
*/
package tm.gfxlibs;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
public class PCXReader
{
/* This is the main-class of the PcxReader. It reads the PCX-Data
* from a stream and converts it into an Image-Class.
*/
public static final int NORMAL = 1;
public static final int RLE = 2;
public static Image loadImage(InputStream in)
{
int pcxheight, pcxwidth;
Image picture = null;
//Header-Data
int manufacturer;
int version;
int encoding;
int bits_per_pixel;
int xmin,ymin;
int xmax,ymax;
int hres;
int vres;
byte[] palette16 = new byte[48];
int reserved;
int color_planes;
int bytes_per_line;
int palette_type;
byte[] filler = new byte[58];
int imagebytes;
try
{
/* In the beginning the Image-Data is read as in the PCX-
* specification.
*/
manufacturer = in.read();
version = in.read();
encoding = in.read();
bits_per_pixel = in.read();
xmin = in.read()+in.read()*256;
ymin = in.read()+in.read()*256;
xmax = in.read()+in.read()*256;
ymax = in.read()+in.read()*256;
hres = in.read()+in.read()*256;
vres = in.read()+in.read()*256;
in.read(palette16);
reserved = in.read();
color_planes = in.read();
bytes_per_line = in.read()+in.read()*256;
palette_type = (short)(in.read()+in.read()*256);
in.read(filler);
pcxwidth = 1+xmax-xmin;
pcxheight = 1+ymax-ymin;
if(pcxwidth % 2 == 1)
{
/* The width of an PCX-Image must be even. That is why the
* width is increased when it was odd before.
*/
pcxwidth++;
}
if(bits_per_pixel == 8 && color_planes == 1)
{
/* If the PCX-file has 256 colors there is a color-palete
* at the end of the file. This is 768b bytes long and
* contains the red- green- and blue-values of the colors.
*/
byte[] pal = new byte[768];
int[] intPal = new int[768];
imagebytes = (pcxwidth*pcxheight);
int[] imageData = new int[imagebytes];
readRLECompressedData(imagebytes, imageData, in);
if(in.available() > 769)
{
while(in.available() > 769)
{
in.read();
}
}
if(in.available() != 769)
{
System.out.println("Error in the palette!");
}
if(in.read()!=12)
{
System.out.println("Error in the palette!");
}
in.read(pal);
in.close();
for(int y = 0; y <767;y++)
{
intPal[y] = (int)(pal[y]);
if(intPal[y] < 0)
{
intPal[y] += 256;
}
}
/* Now the PcxReader converts the imagedata into the format
* of a MemoryImageSource.
*/
int RGBImageData[] = new int[imagebytes];
for(int i = 0; i < imagebytes; i++)
{
int paletteEntry = imageData[i];
if(paletteEntry < 0) paletteEntry += 256;
RGBImageData[i] = new Color(intPal[paletteEntry*3],
intPal[paletteEntry*3+1],
intPal[paletteEntry*3+2]).getRGB();
}
ImageProducer prod = new MemoryImageSource(pcxwidth, pcxheight, RGBImageData, 0, pcxwidth);
picture = Toolkit.getDefaultToolkit().createImage(prod);
}
else if(bits_per_pixel == 8 && color_planes == 3)
{
/* If the picture has 24 bit colors, there are 3 times many
* bytes as many pixels.
*/
imagebytes = (pcxwidth*pcxheight*3);
int[] imageData = new int[imagebytes];
readRLECompressedData(imagebytes, imageData, in);
int RGBImageData[] = new int[imagebytes];
for(int i = 0; i < pcxheight; i++)
{
for(int j = 0; j < pcxwidth; j++)
{
int red = imageData[i*3*pcxwidth+j];
int green = imageData[((i*3)+1)*pcxwidth+j];
int blue = imageData[((i*3)+2)*pcxwidth+j];
RGBImageData[i*pcxwidth+j] = new Color(red,green,blue).getRGB();
}
}
ImageProducer prod = new MemoryImageSource(pcxwidth, pcxheight, RGBImageData, 0, pcxwidth);
picture = Toolkit.getDefaultToolkit().createImage(prod);
}
}
catch (IOException e)
{
System.out.println("Error reading PCX-File!");
}
return picture;
}
private static void readRLECompressedData(int imagebytes, int[] imageData, InputStream in)
throws IOException
{
/* This method reads the compressed data-stream and decompresses
* it in the memory.
*/
int i;
int mode=NORMAL,nbytes=0;
int abyte = 0;
for(i = 0; i<imagebytes;i++)
{
if (mode == NORMAL)
{
abyte = in.read();
if(abyte > 191)
{
nbytes=abyte-192;
abyte =(byte)(in.read());
if (--nbytes > 0)
{
mode = RLE;
}
}
}
else if(--nbytes == 0)
{
mode = NORMAL;
}
imageData[i] = abyte;
if(imageData[i] < 0) imageData[i] += 256;
}
}
}

View file

@ -0,0 +1,621 @@
/**
* PngEncoder takes a Java Image object and creates a byte string which can be saved as a PNG file.
* The Image is presumed to use the DirectColorModel.
*
* Thanks to Jay Denny at KeyPoint Software
* http://www.keypoint.com/
* who let me develop this code on company time.
*
* You may contact me with (probably very-much-needed) improvements,
* comments, and bug fixes at:
*
* david@catcode.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* A copy of the GNU LGPL may be found at
* http://www.gnu.org/copyleft/lesser.html,
*
* @author J. David Eisenberg
* @version 1.4, 31 March 2000
*/
package tm.gfxlibs;
import java.awt.*;
import java.awt.image.*;
import java.util.*;
import java.util.zip.*;
import java.io.*;
public class PngEncoder extends Object
{
/** Constant specifying that alpha channel should be encoded. */
public static final boolean ENCODE_ALPHA=true;
/** Constant specifying that alpha channel should not be encoded. */
public static final boolean NO_ALPHA=false;
/** Constants for filters */
public static final int FILTER_NONE = 0;
public static final int FILTER_SUB = 1;
public static final int FILTER_UP = 2;
public static final int FILTER_LAST = 2;
protected byte[] pngBytes;
protected byte[] priorRow;
protected byte[] leftBytes;
protected Image image;
protected int width, height;
protected int bytePos, maxPos;
protected int hdrPos, dataPos, endPos;
protected CRC32 crc = new CRC32();
protected long crcValue;
protected boolean encodeAlpha;
protected int filter;
protected int bytesPerPixel;
protected int compressionLevel;
/**
* Class constructor
*
*/
public PngEncoder()
{
this( null, false, FILTER_NONE, 0 );
}
/**
* Class constructor specifying Image to encode, with no alpha channel encoding.
*
* @param image A Java Image object which uses the DirectColorModel
* @see java.awt.Image
*/
public PngEncoder( Image image )
{
this(image, false, FILTER_NONE, 0);
}
/**
* Class constructor specifying Image to encode, and whether to encode alpha.
*
* @param image A Java Image object which uses the DirectColorModel
* @param encodeAlpha Encode the alpha channel? false=no; true=yes
* @see java.awt.Image
*/
public PngEncoder( Image image, boolean encodeAlpha )
{
this(image, encodeAlpha, FILTER_NONE, 0);
}
/**
* Class constructor specifying Image to encode, whether to encode alpha, and filter to use.
*
* @param image A Java Image object which uses the DirectColorModel
* @param encodeAlpha Encode the alpha channel? false=no; true=yes
* @param whichFilter 0=none, 1=sub, 2=up
* @see java.awt.Image
*/
public PngEncoder( Image image, boolean encodeAlpha, int whichFilter )
{
this( image, encodeAlpha, whichFilter, 0 );
}
/**
* Class constructor specifying Image source to encode, whether to encode alpha, filter to use, and compression level.
*
* @param image A Java Image object
* @param encodeAlpha Encode the alpha channel? false=no; true=yes
* @param whichFilter 0=none, 1=sub, 2=up
* @param compLevel 0..9
* @see java.awt.Image
*/
public PngEncoder( Image image, boolean encodeAlpha, int whichFilter,
int compLevel)
{
this.image = image;
this.encodeAlpha = encodeAlpha;
setFilter( whichFilter );
if (compLevel >=0 && compLevel <=9)
{
this.compressionLevel = compLevel;
}
}
/**
* Set the image to be encoded
*
* @param image A Java Image object which uses the DirectColorModel
* @see java.awt.Image
* @see java.awt.image.DirectColorModel
*/
public void setImage( Image image )
{
this.image = image;
pngBytes = null;
}
/**
* Creates an array of bytes that is the PNG equivalent of the current image, specifying whether to encode alpha or not.
*
* @param encodeAlpha boolean false=no alpha, true=encode alpha
* @return an array of bytes, or null if there was a problem
*/
public byte[] pngEncode( boolean encodeAlpha )
{
byte[] pngIdBytes = { -119, 80, 78, 71, 13, 10, 26, 10 };
int i;
if (image == null)
{
return null;
}
width = image.getWidth( null );
height = image.getHeight( null );
this.image = image;
/*
* start with an array that is big enough to hold all the pixels
* (plus filter bytes), and an extra 200 bytes for header info
*/
pngBytes = new byte[((width+1) * height * 3) + 200];
/*
* keep track of largest byte written to the array
*/
maxPos = 0;
bytePos = writeBytes( pngIdBytes, 0 );
hdrPos = bytePos;
writeHeader();
dataPos = bytePos;
if (writeImageData())
{
writeEnd();
pngBytes = resizeByteArray( pngBytes, maxPos );
}
else
{
pngBytes = null;
}
return pngBytes;
}
/**
* Creates an array of bytes that is the PNG equivalent of the current image.
* Alpha encoding is determined by its setting in the constructor.
*
* @return an array of bytes, or null if there was a problem
*/
public byte[] pngEncode()
{
return pngEncode( encodeAlpha );
}
/**
* Set the alpha encoding on or off.
*
* @param encodeAlpha false=no, true=yes
*/
public void setEncodeAlpha( boolean encodeAlpha )
{
this.encodeAlpha = encodeAlpha;
}
/**
* Retrieve alpha encoding status.
*
* @return boolean false=no, true=yes
*/
public boolean getEncodeAlpha()
{
return encodeAlpha;
}
/**
* Set the filter to use
*
* @param whichFilter from constant list
*/
public void setFilter( int whichFilter )
{
this.filter = FILTER_NONE;
if ( whichFilter <= FILTER_LAST )
{
this.filter = whichFilter;
}
}
/**
* Retrieve filtering scheme
*
* @return int (see constant list)
*/
public int getFilter()
{
return filter;
}
/**
* Set the compression level to use
*
* @param level 0 through 9
*/
public void setCompressionLevel( int level )
{
if ( level >= 0 && level <= 9)
{
this.compressionLevel = level;
}
}
/**
* Retrieve compression level
*
* @return int in range 0-9
*/
public int getCompressionLevel()
{
return compressionLevel;
}
/**
* Increase or decrease the length of a byte array.
*
* @param array The original array.
* @param newLength The length you wish the new array to have.
* @return Array of newly desired length. If shorter than the
* original, the trailing elements are truncated.
*/
protected byte[] resizeByteArray( byte[] array, int newLength )
{
byte[] newArray = new byte[newLength];
int oldLength = array.length;
System.arraycopy( array, 0, newArray, 0,
Math.min( oldLength, newLength ) );
return newArray;
}
/**
* Write an array of bytes into the pngBytes array.
* Note: This routine has the side effect of updating
* maxPos, the largest element written in the array.
* The array is resized by 1000 bytes or the length
* of the data to be written, whichever is larger.
*
* @param data The data to be written into pngBytes.
* @param offset The starting point to write to.
* @return The next place to be written to in the pngBytes array.
*/
protected int writeBytes( byte[] data, int offset )
{
maxPos = Math.max( maxPos, offset + data.length );
if (data.length + offset > pngBytes.length)
{
pngBytes = resizeByteArray( pngBytes, pngBytes.length +
Math.max( 1000, data.length ) );
}
System.arraycopy( data, 0, pngBytes, offset, data.length );
return offset + data.length;
}
/**
* Write an array of bytes into the pngBytes array, specifying number of bytes to write.
* Note: This routine has the side effect of updating
* maxPos, the largest element written in the array.
* The array is resized by 1000 bytes or the length
* of the data to be written, whichever is larger.
*
* @param data The data to be written into pngBytes.
* @param nBytes The number of bytes to be written.
* @param offset The starting point to write to.
* @return The next place to be written to in the pngBytes array.
*/
protected int writeBytes( byte[] data, int nBytes, int offset )
{
maxPos = Math.max( maxPos, offset + nBytes );
if (nBytes + offset > pngBytes.length)
{
pngBytes = resizeByteArray( pngBytes, pngBytes.length +
Math.max( 1000, nBytes ) );
}
System.arraycopy( data, 0, pngBytes, offset, nBytes );
return offset + nBytes;
}
/**
* Write a two-byte integer into the pngBytes array at a given position.
*
* @param n The integer to be written into pngBytes.
* @param offset The starting point to write to.
* @return The next place to be written to in the pngBytes array.
*/
protected int writeInt2( int n, int offset )
{
byte[] temp = { (byte)((n >> 8) & 0xff),
(byte) (n & 0xff) };
return writeBytes( temp, offset );
}
/**
* Write a four-byte integer into the pngBytes array at a given position.
*
* @param n The integer to be written into pngBytes.
* @param offset The starting point to write to.
* @return The next place to be written to in the pngBytes array.
*/
protected int writeInt4( int n, int offset )
{
byte[] temp = { (byte)((n >> 24) & 0xff),
(byte) ((n >> 16) & 0xff ),
(byte) ((n >> 8) & 0xff ),
(byte) ( n & 0xff ) };
return writeBytes( temp, offset );
}
/**
* Write a single byte into the pngBytes array at a given position.
*
* @param n The integer to be written into pngBytes.
* @param offset The starting point to write to.
* @return The next place to be written to in the pngBytes array.
*/
protected int writeByte( int b, int offset )
{
byte[] temp = { (byte) b };
return writeBytes( temp, offset );
}
/**
* Write a string into the pngBytes array at a given position.
* This uses the getBytes method, so the encoding used will
* be its default.
*
* @param n The integer to be written into pngBytes.
* @param offset The starting point to write to.
* @return The next place to be written to in the pngBytes array.
* @see java.lang.String#getBytes()
*/
protected int writeString( String s, int offset )
{
return writeBytes( s.getBytes(), offset );
}
/**
* Write a PNG "IHDR" chunk into the pngBytes array.
*/
protected void writeHeader()
{
int startPos;
startPos = bytePos = writeInt4( 13, bytePos );
bytePos = writeString( "IHDR", bytePos );
width = image.getWidth( null );
height = image.getHeight( null );
bytePos = writeInt4( width, bytePos );
bytePos = writeInt4( height, bytePos );
bytePos = writeByte( 8, bytePos ); // bit depth
bytePos = writeByte( (encodeAlpha) ? 6 : 2, bytePos ); // direct model
bytePos = writeByte( 0, bytePos ); // compression method
bytePos = writeByte( 0, bytePos ); // filter method
bytePos = writeByte( 0, bytePos ); // no interlace
crc.reset();
crc.update( pngBytes, startPos, bytePos-startPos );
crcValue = crc.getValue();
bytePos = writeInt4( (int) crcValue, bytePos );
}
/**
* Perform "sub" filtering on the given row.
* Uses temporary array leftBytes to store the original values
* of the previous pixels. The array is 16 bytes long, which
* will easily hold two-byte samples plus two-byte alpha.
*
* @param pixels The array holding the scan lines being built
* @param startPos Starting position within pixels of bytes to be filtered.
* @param width Width of a scanline in pixels.
*/
protected void filterSub( byte[] pixels, int startPos, int width )
{
int i;
int offset = bytesPerPixel;
int actualStart = startPos + offset;
int nBytes = width * bytesPerPixel;
int leftInsert = offset;
int leftExtract = 0;
byte current_byte;
for (i=actualStart; i < startPos + nBytes; i++)
{
leftBytes[leftInsert] = pixels[i];
pixels[i] = (byte) ((pixels[i] - leftBytes[leftExtract]) % 256);
leftInsert = (leftInsert+1) % 0x0f;
leftExtract = (leftExtract + 1) % 0x0f;
}
}
/**
* Perform "up" filtering on the given row.
* Side effect: refills the prior row with current row
*
* @param pixels The array holding the scan lines being built
* @param startPos Starting position within pixels of bytes to be filtered.
* @param width Width of a scanline in pixels.
*/
protected void filterUp( byte[] pixels, int startPos, int width )
{
int i, nBytes;
byte current_byte;
nBytes = width * bytesPerPixel;
for (i=0; i < nBytes; i++)
{
current_byte = pixels[startPos + i];
pixels[startPos + i] = (byte) ((pixels[startPos + i] - priorRow[i]) % 256);
priorRow[i] = current_byte;
}
}
/**
* Write the image data into the pngBytes array.
* This will write one or more PNG "IDAT" chunks. In order
* to conserve memory, this method grabs as many rows as will
* fit into 32K bytes, or the whole image; whichever is less.
*
*
* @return true if no errors; false if error grabbing pixels
*/
protected boolean writeImageData()
{
int rowsLeft = height; // number of rows remaining to write
int startRow = 0; // starting row to process this time through
int nRows; // how many rows to grab at a time
byte[] scanLines; // the scan lines to be compressed
int scanPos; // where we are in the scan lines
int startPos; // where this line's actual pixels start (used for filtering)
byte[] compressedLines; // the resultant compressed lines
int nCompressed; // how big is the compressed area?
int depth; // color depth ( handle only 8 or 32 )
PixelGrabber pg;
bytesPerPixel = (encodeAlpha) ? 4 : 3;
Deflater scrunch = new Deflater( compressionLevel );
ByteArrayOutputStream outBytes =
new ByteArrayOutputStream(1024);
DeflaterOutputStream compBytes =
new DeflaterOutputStream( outBytes, scrunch );
try
{
while (rowsLeft > 0)
{
nRows = Math.min( 32767 / (width*(bytesPerPixel+1)), rowsLeft );
// nRows = rowsLeft;
int[] pixels = new int[width * nRows];
pg = new PixelGrabber(image, 0, startRow,
width, nRows, pixels, 0, width);
try {
pg.grabPixels();
}
catch (Exception e) {
System.err.println("interrupted waiting for pixels!");
return false;
}
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
return false;
}
/*
* Create a data chunk. scanLines adds "nRows" for
* the filter bytes.
*/
scanLines = new byte[width * nRows * bytesPerPixel + nRows];
if (filter == FILTER_SUB)
{
leftBytes = new byte[16];
}
if (filter == FILTER_UP)
{
priorRow = new byte[width*bytesPerPixel];
}
scanPos = 0;
startPos = 1;
for (int i=0; i<width*nRows; i++)
{
if (i % width == 0)
{
scanLines[scanPos++] = (byte) filter;
startPos = scanPos;
}
scanLines[scanPos++] = (byte) ((pixels[i] >> 16) & 0xff);
scanLines[scanPos++] = (byte) ((pixels[i] >> 8) & 0xff);
scanLines[scanPos++] = (byte) ((pixels[i] ) & 0xff);
if (encodeAlpha)
{
scanLines[scanPos++] = (byte) ((pixels[i] >> 24) & 0xff );
}
if ((i % width == width-1) && (filter != FILTER_NONE))
{
if (filter == FILTER_SUB)
{
filterSub( scanLines, startPos, width );
}
if (filter == FILTER_UP)
{
filterUp( scanLines, startPos, width );
}
}
}
/*
* Write these lines to the output area
*/
compBytes.write( scanLines, 0, scanPos );
startRow += nRows;
rowsLeft -= nRows;
}
compBytes.close();
/*
* Write the compressed bytes
*/
compressedLines = outBytes.toByteArray();
nCompressed = compressedLines.length;
crc.reset();
bytePos = writeInt4( nCompressed, bytePos );
bytePos = writeString("IDAT", bytePos );
crc.update("IDAT".getBytes());
bytePos = writeBytes( compressedLines, nCompressed, bytePos );
crc.update( compressedLines, 0, nCompressed );
crcValue = crc.getValue();
bytePos = writeInt4( (int) crcValue, bytePos );
scrunch.finish();
return true;
}
catch (IOException e)
{
System.err.println( e.toString());
return false;
}
}
/**
* Write a PNG "IEND" chunk into the pngBytes array.
*/
protected void writeEnd()
{
bytePos = writeInt4( 0, bytePos );
bytePos = writeString( "IEND", bytePos );
crc.reset();
crc.update("IEND".getBytes());
crcValue = crc.getValue();
bytePos = writeInt4( (int) crcValue, bytePos );
}
}

View file

@ -0,0 +1,703 @@
/*
* @(#)Quantize.java 0.90 9/19/00 Adam Doppelt
*/
/**
* An efficient color quantization algorithm, adapted from the C++
* implementation quantize.c in <a
* href="http://www.imagemagick.org/">ImageMagick</a>. The pixels for
* an image are placed into an oct tree. The oct tree is reduced in
* size, and the pixels from the original image are reassigned to the
* nodes in the reduced tree.<p>
*
* Here is the copyright notice from ImageMagick:
*
* <pre>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Permission is hereby granted, free of charge, to any person obtaining a %
% copy of this software and associated documentation files ("ImageMagick"), %
% to deal in ImageMagick without restriction, including without limitation %
% the rights to use, copy, modify, merge, publish, distribute, sublicense, %
% and/or sell copies of ImageMagick, and to permit persons to whom the %
% ImageMagick is furnished to do so, subject to the following conditions: %
% %
% The above copyright notice and this permission notice shall be included in %
% all copies or substantial portions of ImageMagick. %
% %
% The software is provided "as is", without warranty of any kind, express or %
% implied, including but not limited to the warranties of merchantability, %
% fitness for a particular purpose and noninfringement. In no event shall %
% E. I. du Pont de Nemours and Company be liable for any claim, damages or %
% other liability, whether in an action of contract, tort or otherwise, %
% arising from, out of or in connection with ImageMagick or the use or other %
% dealings in ImageMagick. %
% %
% Except as contained in this notice, the name of the E. I. du Pont de %
% Nemours and Company shall not be used in advertising or otherwise to %
% promote the sale, use or other dealings in ImageMagick without prior %
% written authorization from the E. I. du Pont de Nemours and Company. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
</pre>
*
*
* @version 0.90 19 Sep 2000
* @author <a href="http://www.gurge.com/amd/">Adam Doppelt</a>
*/
package tm.gfxlibs;
public class Quantize {
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE %
% Q Q U U A A NN N T I ZZ E %
% Q Q U U AAAAA N N N T I ZZZ EEEEE %
% Q QQ U U A A N NN T I ZZ E %
% QQQQ UUU A A N N T IIIII ZZZZZ EEEEE %
% %
% %
% Reduce the Number of Unique Colors in an Image %
% %
% %
% Software Design %
% John Cristy %
% July 1992 %
% %
% %
% Copyright 1998 E. I. du Pont de Nemours and Company %
% %
% Permission is hereby granted, free of charge, to any person obtaining a %
% copy of this software and associated documentation files ("ImageMagick"), %
% to deal in ImageMagick without restriction, including without limitation %
% the rights to use, copy, modify, merge, publish, distribute, sublicense, %
% and/or sell copies of ImageMagick, and to permit persons to whom the %
% ImageMagick is furnished to do so, subject to the following conditions: %
% %
% The above copyright notice and this permission notice shall be included in %
% all copies or substantial portions of ImageMagick. %
% %
% The software is provided "as is", without warranty of any kind, express or %
% implied, including but not limited to the warranties of merchantability, %
% fitness for a particular purpose and noninfringement. In no event shall %
% E. I. du Pont de Nemours and Company be liable for any claim, damages or %
% other liability, whether in an action of contract, tort or otherwise, %
% arising from, out of or in connection with ImageMagick or the use or other %
% dealings in ImageMagick. %
% %
% Except as contained in this notice, the name of the E. I. du Pont de %
% Nemours and Company shall not be used in advertising or otherwise to %
% promote the sale, use or other dealings in ImageMagick without prior %
% written authorization from the E. I. du Pont de Nemours and Company. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Realism in computer graphics typically requires using 24 bits/pixel to
% generate an image. Yet many graphic display devices do not contain
% the amount of memory necessary to match the spatial and color
% resolution of the human eye. The QUANTIZE program takes a 24 bit
% image and reduces the number of colors so it can be displayed on
% raster device with less bits per pixel. In most instances, the
% quantized image closely resembles the original reference image.
%
% A reduction of colors in an image is also desirable for image
% transmission and real-time animation.
%
% Function Quantize takes a standard RGB or monochrome images and quantizes
% them down to some fixed number of colors.
%
% For purposes of color allocation, an image is a set of n pixels, where
% each pixel is a point in RGB space. RGB space is a 3-dimensional
% vector space, and each pixel, pi, is defined by an ordered triple of
% red, green, and blue coordinates, (ri, gi, bi).
%
% Each primary color component (red, green, or blue) represents an
% intensity which varies linearly from 0 to a maximum value, cmax, which
% corresponds to full saturation of that color. Color allocation is
% defined over a domain consisting of the cube in RGB space with
% opposite vertices at (0,0,0) and (cmax,cmax,cmax). QUANTIZE requires
% cmax = 255.
%
% The algorithm maps this domain onto a tree in which each node
% represents a cube within that domain. In the following discussion
% these cubes are defined by the coordinate of two opposite vertices:
% The vertex nearest the origin in RGB space and the vertex farthest
% from the origin.
%
% The tree's root node represents the the entire domain, (0,0,0) through
% (cmax,cmax,cmax). Each lower level in the tree is generated by
% subdividing one node's cube into eight smaller cubes of equal size.
% This corresponds to bisecting the parent cube with planes passing
% through the midpoints of each edge.
%
% The basic algorithm operates in three phases: Classification,
% Reduction, and Assignment. Classification builds a color
% description tree for the image. Reduction collapses the tree until
% the number it represents, at most, the number of colors desired in the
% output image. Assignment defines the output image's color map and
% sets each pixel's color by reclassification in the reduced tree.
% Our goal is to minimize the numerical discrepancies between the original
% colors and quantized colors (quantization error).
%
% Classification begins by initializing a color description tree of
% sufficient depth to represent each possible input color in a leaf.
% However, it is impractical to generate a fully-formed color
% description tree in the classification phase for realistic values of
% cmax. If colors components in the input image are quantized to k-bit
% precision, so that cmax= 2k-1, the tree would need k levels below the
% root node to allow representing each possible input color in a leaf.
% This becomes prohibitive because the tree's total number of nodes is
% 1 + sum(i=1,k,8k).
%
% A complete tree would require 19,173,961 nodes for k = 8, cmax = 255.
% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
% Initializes data structures for nodes only as they are needed; (2)
% Chooses a maximum depth for the tree as a function of the desired
% number of colors in the output image (currently log2(colormap size)).
%
% For each pixel in the input image, classification scans downward from
% the root of the color description tree. At each level of the tree it
% identifies the single node which represents a cube in RGB space
% containing the pixel's color. It updates the following data for each
% such node:
%
% n1: Number of pixels whose color is contained in the RGB cube
% which this node represents;
%
% n2: Number of pixels whose color is not represented in a node at
% lower depth in the tree; initially, n2 = 0 for all nodes except
% leaves of the tree.
%
% Sr, Sg, Sb: Sums of the red, green, and blue component values for
% all pixels not classified at a lower depth. The combination of
% these sums and n2 will ultimately characterize the mean color of a
% set of pixels represented by this node.
%
% E: The distance squared in RGB space between each pixel contained
% within a node and the nodes' center. This represents the quantization
% error for a node.
%
% Reduction repeatedly prunes the tree until the number of nodes with
% n2 > 0 is less than or equal to the maximum number of colors allowed
% in the output image. On any given iteration over the tree, it selects
% those nodes whose E count is minimal for pruning and merges their
% color statistics upward. It uses a pruning threshold, Ep, to govern
% node selection as follows:
%
% Ep = 0
% while number of nodes with (n2 > 0) > required maximum number of colors
% prune all nodes such that E <= Ep
% Set Ep to minimum E in remaining nodes
%
% This has the effect of minimizing any quantization error when merging
% two nodes together.
%
% When a node to be pruned has offspring, the pruning procedure invokes
% itself recursively in order to prune the tree from the leaves upward.
% n2, Sr, Sg, and Sb in a node being pruned are always added to the
% corresponding data in that node's parent. This retains the pruned
% node's color characteristics for later averaging.
%
% For each node, n2 pixels exist for which that node represents the
% smallest volume in RGB space containing those pixel's colors. When n2
% > 0 the node will uniquely define a color in the output image. At the
% beginning of reduction, n2 = 0 for all nodes except a the leaves of
% the tree which represent colors present in the input image.
%
% The other pixel count, n1, indicates the total number of colors
% within the cubic volume which the node represents. This includes n1 -
% n2 pixels whose colors should be defined by nodes at a lower level in
% the tree.
%
% Assignment generates the output image from the pruned tree. The
% output image consists of two parts: (1) A color map, which is an
% array of color descriptions (RGB triples) for each color present in
% the output image; (2) A pixel array, which represents each pixel as
% an index into the color map array.
%
% First, the assignment phase makes one pass over the pruned color
% description tree to establish the image's color map. For each node
% with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the
% mean color of all pixels that classify no lower than this node. Each
% of these colors becomes an entry in the color map.
%
% Finally, the assignment phase reclassifies each pixel in the pruned
% tree to identify the deepest node containing the pixel's color. The
% pixel's value in the pixel array becomes the index of this node's mean
% color in the color map.
%
% With the permission of USC Information Sciences Institute, 4676 Admiralty
% Way, Marina del Rey, California 90292, this code was adapted from module
% ALCOLS written by Paul Raveling.
%
% The names of ISI and USC are not used in advertising or publicity
% pertaining to distribution of the software without prior specific
% written permission from ISI.
%
*/
final static boolean QUICK = true;
final static int MAX_RGB = 255;
final static int MAX_NODES = 266817;
final static int MAX_TREE_DEPTH = 8;
// these are precomputed in advance
static int SQUARES[];
static int SHIFT[];
static {
SQUARES = new int[MAX_RGB + MAX_RGB + 1];
for (int i= -MAX_RGB; i <= MAX_RGB; i++) {
SQUARES[i + MAX_RGB] = i * i;
}
SHIFT = new int[MAX_TREE_DEPTH + 1];
for (int i = 0; i < MAX_TREE_DEPTH + 1; ++i) {
SHIFT[i] = 1 << (15 - i);
}
}
/**
* Reduce the image to the given number of colors. The pixels are
* reduced in place.
* @return The new color palette.
*/
public static int[] quantizeImage(int pixels[][], int max_colors) {
Cube cube = new Cube(pixels, max_colors);
cube.classification();
cube.reduction();
cube.assignment();
return cube.colormap;
}
static class Cube {
int pixels[][];
int max_colors;
int colormap[];
Node root;
int depth;
// counter for the number of colors in the cube. this gets
// recalculated often.
int colors;
// counter for the number of nodes in the tree
int nodes;
Cube(int pixels[][], int max_colors) {
this.pixels = pixels;
this.max_colors = max_colors;
int i = max_colors;
// tree_depth = log max_colors
// 4
for (depth = 1; i != 0; depth++) {
i /= 4;
}
if (depth > 1) {
--depth;
}
if (depth > MAX_TREE_DEPTH) {
depth = MAX_TREE_DEPTH;
} else if (depth < 2) {
depth = 2;
}
root = new Node(this);
}
/*
* Procedure Classification begins by initializing a color
* description tree of sufficient depth to represent each
* possible input color in a leaf. However, it is impractical
* to generate a fully-formed color description tree in the
* classification phase for realistic values of cmax. If
* colors components in the input image are quantized to k-bit
* precision, so that cmax= 2k-1, the tree would need k levels
* below the root node to allow representing each possible
* input color in a leaf. This becomes prohibitive because the
* tree's total number of nodes is 1 + sum(i=1,k,8k).
*
* A complete tree would require 19,173,961 nodes for k = 8,
* cmax = 255. Therefore, to avoid building a fully populated
* tree, QUANTIZE: (1) Initializes data structures for nodes
* only as they are needed; (2) Chooses a maximum depth for
* the tree as a function of the desired number of colors in
* the output image (currently log2(colormap size)).
*
* For each pixel in the input image, classification scans
* downward from the root of the color description tree. At
* each level of the tree it identifies the single node which
* represents a cube in RGB space containing It updates the
* following data for each such node:
*
* number_pixels : Number of pixels whose color is contained
* in the RGB cube which this node represents;
*
* unique : Number of pixels whose color is not represented
* in a node at lower depth in the tree; initially, n2 = 0
* for all nodes except leaves of the tree.
*
* total_red/green/blue : Sums of the red, green, and blue
* component values for all pixels not classified at a lower
* depth. The combination of these sums and n2 will
* ultimately characterize the mean color of a set of pixels
* represented by this node.
*/
void classification() {
int pixels[][] = this.pixels;
int width = pixels.length;
int height = pixels[0].length;
// convert to indexed color
for (int x = width; x-- > 0; ) {
for (int y = height; y-- > 0; ) {
int pixel = pixels[x][y];
int red = (pixel >> 16) & 0xFF;
int green = (pixel >> 8) & 0xFF;
int blue = (pixel >> 0) & 0xFF;
// a hard limit on the number of nodes in the tree
if (nodes > MAX_NODES) {
System.out.println("pruning");
root.pruneLevel();
--depth;
}
// walk the tree to depth, increasing the
// number_pixels count for each node
Node node = root;
for (int level = 1; level <= depth; ++level) {
int id = (((red > node.mid_red ? 1 : 0) << 0) |
((green > node.mid_green ? 1 : 0) << 1) |
((blue > node.mid_blue ? 1 : 0) << 2));
if (node.child[id] == null) {
new Node(node, id, level);
}
node = node.child[id];
node.number_pixels += SHIFT[level];
}
++node.unique;
node.total_red += red;
node.total_green += green;
node.total_blue += blue;
}
}
}
/*
* reduction repeatedly prunes the tree until the number of
* nodes with unique > 0 is less than or equal to the maximum
* number of colors allowed in the output image.
*
* When a node to be pruned has offspring, the pruning
* procedure invokes itself recursively in order to prune the
* tree from the leaves upward. The statistics of the node
* being pruned are always added to the corresponding data in
* that node's parent. This retains the pruned node's color
* characteristics for later averaging.
*/
void reduction() {
int threshold = 1;
while (colors > max_colors) {
colors = 0;
threshold = root.reduce(threshold, Integer.MAX_VALUE);
}
}
/**
* The result of a closest color search.
*/
static class Search {
int distance;
int color_number;
}
/*
* Procedure assignment generates the output image from the
* pruned tree. The output image consists of two parts: (1) A
* color map, which is an array of color descriptions (RGB
* triples) for each color present in the output image; (2) A
* pixel array, which represents each pixel as an index into
* the color map array.
*
* First, the assignment phase makes one pass over the pruned
* color description tree to establish the image's color map.
* For each node with n2 > 0, it divides Sr, Sg, and Sb by n2.
* This produces the mean color of all pixels that classify no
* lower than this node. Each of these colors becomes an entry
* in the color map.
*
* Finally, the assignment phase reclassifies each pixel in
* the pruned tree to identify the deepest node containing the
* pixel's color. The pixel's value in the pixel array becomes
* the index of this node's mean color in the color map.
*/
void assignment() {
colormap = new int[colors];
colors = 0;
root.colormap();
int pixels[][] = this.pixels;
int width = pixels.length;
int height = pixels[0].length;
Search search = new Search();
// convert to indexed color
for (int x = width; x-- > 0; ) {
for (int y = height; y-- > 0; ) {
int pixel = pixels[x][y];
int red = (pixel >> 16) & 0xFF;
int green = (pixel >> 8) & 0xFF;
int blue = (pixel >> 0) & 0xFF;
// walk the tree to find the cube containing that color
Node node = root;
for ( ; ; ) {
int id = (((red > node.mid_red ? 1 : 0) << 0) |
((green > node.mid_green ? 1 : 0) << 1) |
((blue > node.mid_blue ? 1 : 0) << 2) );
if (node.child[id] == null) {
break;
}
node = node.child[id];
}
if (QUICK) {
// if QUICK is set, just use that
// node. Strictly speaking, this isn't
// necessarily best match.
pixels[x][y] = node.color_number;
} else {
// Find the closest color.
search.distance = Integer.MAX_VALUE;
node.parent.closestColor(red, green, blue, search);
pixels[x][y] = search.color_number;
}
}
}
}
/**
* A single Node in the tree.
*/
static class Node {
Cube cube;
// parent node
Node parent;
// child nodes
Node child[];
int nchild;
// our index within our parent
int id;
// our level within the tree
int level;
// our color midpoint
int mid_red;
int mid_green;
int mid_blue;
// the pixel count for this node and all children
int number_pixels;
// the pixel count for this node
int unique;
// the sum of all pixels contained in this node
int total_red;
int total_green;
int total_blue;
// used to build the colormap
int color_number;
Node(Cube cube) {
this.cube = cube;
this.parent = this;
this.child = new Node[8];
this.id = 0;
this.level = 0;
this.number_pixels = Integer.MAX_VALUE;
this.mid_red = (MAX_RGB + 1) >> 1;
this.mid_green = (MAX_RGB + 1) >> 1;
this.mid_blue = (MAX_RGB + 1) >> 1;
}
Node(Node parent, int id, int level) {
this.cube = parent.cube;
this.parent = parent;
this.child = new Node[8];
this.id = id;
this.level = level;
// add to the cube
++cube.nodes;
if (level == cube.depth) {
++cube.colors;
}
// add to the parent
++parent.nchild;
parent.child[id] = this;
// figure out our midpoint
int bi = (1 << (MAX_TREE_DEPTH - level)) >> 1;
mid_red = parent.mid_red + ((id & 1) > 0 ? bi : -bi);
mid_green = parent.mid_green + ((id & 2) > 0 ? bi : -bi);
mid_blue = parent.mid_blue + ((id & 4) > 0 ? bi : -bi);
}
/**
* Remove this child node, and make sure our parent
* absorbs our pixel statistics.
*/
void pruneChild() {
--parent.nchild;
parent.unique += unique;
parent.total_red += total_red;
parent.total_green += total_green;
parent.total_blue += total_blue;
parent.child[id] = null;
--cube.nodes;
cube = null;
parent = null;
}
/**
* Prune the lowest layer of the tree.
*/
void pruneLevel() {
if (nchild != 0) {
for (int id = 0; id < 8; id++) {
if (child[id] != null) {
child[id].pruneLevel();
}
}
}
if (level == cube.depth) {
pruneChild();
}
}
/**
* Remove any nodes that have fewer than threshold
* pixels. Also, as long as we're walking the tree:
*
* - figure out the color with the fewest pixels
* - recalculate the total number of colors in the tree
*/
int reduce(int threshold, int next_threshold) {
if (nchild != 0) {
for (int id = 0; id < 8; id++) {
if (child[id] != null) {
next_threshold = child[id].reduce(threshold, next_threshold);
}
}
}
if (number_pixels <= threshold) {
pruneChild();
} else {
if (unique != 0) {
cube.colors++;
}
if (number_pixels < next_threshold) {
next_threshold = number_pixels;
}
}
return next_threshold;
}
/*
* colormap traverses the color cube tree and notes each
* colormap entry. A colormap entry is any node in the
* color cube tree where the number of unique colors is
* not zero.
*/
void colormap() {
if (nchild != 0) {
for (int id = 0; id < 8; id++) {
if (child[id] != null) {
child[id].colormap();
}
}
}
if (unique != 0) {
int r = ((total_red + (unique >> 1)) / unique);
int g = ((total_green + (unique >> 1)) / unique);
int b = ((total_blue + (unique >> 1)) / unique);
cube.colormap[cube.colors] = ((( 0xFF) << 24) |
((r & 0xFF) << 16) |
((g & 0xFF) << 8) |
((b & 0xFF) << 0));
color_number = cube.colors++;
}
}
/* ClosestColor traverses the color cube tree at a
* particular node and determines which colormap entry
* best represents the input color.
*/
void closestColor(int red, int green, int blue, Search search) {
if (nchild != 0) {
for (int id = 0; id < 8; id++) {
if (child[id] != null) {
child[id].closestColor(red, green, blue, search);
}
}
}
if (unique != 0) {
int color = cube.colormap[color_number];
int distance = distance(color, red, green, blue);
if (distance < search.distance) {
search.distance = distance;
search.color_number = color_number;
}
}
}
/**
* Figure out the distance between this node and som color.
*/
final static int distance(int color, int r, int g, int b) {
return (SQUARES[((color >> 16) & 0xFF) - r + MAX_RGB] +
SQUARES[((color >> 8) & 0xFF) - g + MAX_RGB] +
SQUARES[((color >> 0) & 0xFF) - b + MAX_RGB]);
}
public String toString() {
StringBuffer buf = new StringBuffer();
if (parent == this) {
buf.append("root");
} else {
buf.append("node");
}
buf.append(' ');
buf.append(level);
buf.append(" [");
buf.append(mid_red);
buf.append(',');
buf.append(mid_green);
buf.append(',');
buf.append(mid_blue);
buf.append(']');
return new String(buf);
}
}
}
}

BIN
src/tm/icons/Add24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

BIN
src/tm/icons/Back24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

BIN
src/tm/icons/Brush24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
src/tm/icons/Copy24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

BIN
src/tm/icons/Cut24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
src/tm/icons/DecWidth24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

BIN
src/tm/icons/Delete24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

BIN
src/tm/icons/Down24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

BIN
src/tm/icons/Dropper24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

BIN
src/tm/icons/End24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/tm/icons/Export24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

BIN
src/tm/icons/Fill24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

BIN
src/tm/icons/Flip24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

BIN
src/tm/icons/Forward24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

BIN
src/tm/icons/Goto24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/tm/icons/Home24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/tm/icons/Icon24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 B

BIN
src/tm/icons/Import24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

BIN
src/tm/icons/IncWidth24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 B

BIN
src/tm/icons/Line24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

BIN
src/tm/icons/Minus24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

BIN
src/tm/icons/Mirror24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

BIN
src/tm/icons/Mover24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

BIN
src/tm/icons/New24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

BIN
src/tm/icons/Open24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

BIN
src/tm/icons/Paste24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

BIN
src/tm/icons/Play24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

BIN
src/tm/icons/Plus24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

BIN
src/tm/icons/Redo24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

BIN
src/tm/icons/Rewind24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
src/tm/icons/Save24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

BIN
src/tm/icons/SaveAll24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

BIN
src/tm/icons/SaveAs24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

BIN
src/tm/icons/ShiftUp24.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Some files were not shown because too many files have changed in this diff Show more