diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib index de94f31..1d65f2f 100644 --- a/English.lproj/MainMenu.xib +++ b/English.lproj/MainMenu.xib @@ -2,7 +2,7 @@ 1060 - 10H574 + 10J869 823 1038.35 461.00 @@ -12,10 +12,10 @@ YES - - + - + + YES @@ -1224,7 +1224,7 @@ 13 2 - {{142, 445}, {524, 431}} + {{142, 420}, {524, 456}} 1948778496 Tile Cutter NSWindow @@ -1239,7 +1239,7 @@ 292 - {{20, 17}, {48, 22}} + {{20, 42}, {48, 22}} YES YES @@ -1346,7 +1346,7 @@ 292 - {{73, 22}, {12, 17}} + {{73, 47}, {12, 17}} YES @@ -1375,7 +1375,7 @@ 292 - {{90, 17}, {48, 22}} + {{90, 42}, {48, 22}} YES YES @@ -1441,7 +1441,7 @@ 289 - {{441, 11}, {69, 32}} + {{441, 36}, {69, 32}} YES @@ -1468,7 +1468,7 @@ NSColor pasteboard type - {{395, 16}, {44, 23}} + {{395, 41}, {44, 23}} YES YES @@ -1480,7 +1480,7 @@ 289 - {{307, 20}, {83, 17}} + {{307, 45}, {83, 17}} YES @@ -1508,7 +1508,7 @@ NeXT TIFF v4.0 pasteboard type - {{20, 47}, {484, 367}} + {{20, 72}, {484, 367}} YES @@ -1524,7 +1524,7 @@ 289 - {{195, 20}, {103, 18}} + {{195, 45}, {103, 18}} YES @@ -1535,11 +1535,11 @@ 1211912703 2 - + NSImage NSSwitch - + NSSwitch @@ -1548,8 +1548,30 @@ 25 + + + 289 + {{195, 25}, {194, 18}} + + YES + + -2080244224 + 0 + Skip Transparent Tiles + + + 1211912703 + 2 + + + + + 200 + 25 + + - {524, 431} + {524, 456} {{0, 0}, {1920, 1058}} @@ -1561,6 +1583,10 @@ NSFontManager + + YES + skipTransparentTiles + YES @@ -2683,6 +2709,38 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 647 + + + valueChanged: + + + + 654 + + + + value: values.skipTransparentTiles + + + + + + value: values.skipTransparentTiles + value + values.skipTransparentTiles + 2 + + + 656 + + + + skipCheckbox + + + + 657 + @@ -3175,6 +3233,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + @@ -3906,6 +3965,20 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + 652 + + + YES + + + + + + 653 + + + @@ -4178,6 +4251,9 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA 610.IBPluginDependency 612.IBPluginDependency 639.IBViewBoundsToFrameTransform + 652.IBPluginDependency + 652.IBViewBoundsToFrameTransform + 653.IBPluginDependency 72.IBPluginDependency 72.ImportedFromIB2 79.IBPluginDependency @@ -4290,7 +4366,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin {{525, 802}, {197, 73}} - {{194, 943}, {441, 20}} + {{839, 736}, {441, 20}} com.apple.InterfaceBuilder.CocoaPlugin {74, 862} @@ -4315,10 +4391,10 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin - {{753, 18}, {524, 431}} + {{420, -31}, {524, 456}} com.apple.InterfaceBuilder.CocoaPlugin - {{753, 18}, {524, 431}} + {{420, -31}, {524, 456}} {{33, 99}, {480, 360}} @@ -4450,31 +4526,31 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABDEAAAwbAAAA + P4AAAL+AAABDQwAAwnQAAA com.apple.InterfaceBuilder.CocoaPlugin - {{206, 760}, {207, 183}} + {{851, 553}, {207, 183}} com.apple.InterfaceBuilder.CocoaPlugin {{23, 794}, {245, 183}} - {{588, 803}, {456, 105}} + {{824, 651}, {456, 105}} com.apple.InterfaceBuilder.CocoaPlugin - {{588, 803}, {456, 105}} + {{824, 651}, {456, 105}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - {{201, 522}, {310, 176}} + {{329, 522}, {310, 176}} com.apple.InterfaceBuilder.CocoaPlugin - {{201, 522}, {310, 176}} + {{329, 522}, {310, 176}} com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - P4AAAL+AAABBkAAAwuAAAA + P4AAAL+AAABB0AAAwuAAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -4482,6 +4558,11 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA AUHIAABB0AAAA com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDEAAAwbAAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -4513,7 +4594,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA - 647 + 657 @@ -4539,6 +4620,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA guideCheckbox guideColorWell saveButton + skipCheckbox tileHeightField tileWidthField @@ -4547,6 +4629,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA NSButton NSColorWell NSButton + NSButton NSTextField NSTextField @@ -4558,6 +4641,7 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA guideCheckbox guideColorWell saveButton + skipCheckbox tileHeightField tileWidthField @@ -4575,6 +4659,10 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA saveButton NSButton + + skipCheckbox + NSButton + tileHeightField NSTextField @@ -4712,6 +4800,572 @@ AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + YES + + NSActionCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSActionCell.h + + + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSApplicationScripting.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSColorPanel.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSHelpManager.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSPageLayout.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSUserInterfaceItemSearching.h + + + + NSBox + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSBox.h + + + + NSBrowser + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSBrowser.h + + + + NSButton + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSButton.h + + + + NSButtonCell + NSActionCell + + IBFrameworkSource + AppKit.framework/Headers/NSButtonCell.h + + + + NSCell + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSCell.h + + + + NSColorWell + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSColorWell.h + + + + NSControl + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSController + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSController.h + + + + NSFontManager + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontManager.h + + + + NSFormatter + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFormatter.h + + + + NSImageCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSImageCell.h + + + + NSMatrix + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSMatrix.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSMenuItem + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenuItem.h + + + + NSMovieView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSMovieView.h + + + + NSNumberFormatter + NSFormatter + + IBFrameworkSource + Foundation.framework/Headers/NSNumberFormatter.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSAccessibility.h + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDictionaryController.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDragging.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontPanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSKeyValueBinding.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSNibLoading.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSOutlineView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSPasteboard.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSSavePanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSToolbarItem.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObjectScripting.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPortCoder.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptObjectSpecifiers.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptWhoseTests.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLDownload.h + + + + NSProgressIndicator + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSProgressIndicator.h + + + + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSInterfaceStyle.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + NSString + + + + NSString + + IBFrameworkSource + AppKit.framework/Headers/NSStringDrawing.h + + + + NSString + + IBFrameworkSource + Foundation.framework/Headers/NSPathUtilities.h + + + + NSString + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSString.h + + + + NSString + + + + NSTableView + NSControl + + + + NSText + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSText.h + + + + NSTextField + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSTextField.h + + + + NSTextFieldCell + NSActionCell + + IBFrameworkSource + AppKit.framework/Headers/NSTextFieldCell.h + + + + NSTextView + NSText + + IBFrameworkSource + AppKit.framework/Headers/NSTextView.h + + + + NSUserDefaultsController + NSController + + IBFrameworkSource + AppKit.framework/Headers/NSUserDefaultsController.h + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSClipView.h + + + + NSView + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSRulerView.h + + + + NSView + NSResponder + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSDrawer.h + + + + NSWindow + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSWindow.h + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSWindowScripting.h + + + 0 IBCocoaFramework diff --git a/NSBitmapImageRep-Tile.h b/NSBitmapImageRep-Tile.h index 46384bf..47d9f05 100644 --- a/NSBitmapImageRep-Tile.h +++ b/NSBitmapImageRep-Tile.h @@ -10,7 +10,33 @@ @interface NSBitmapImageRep(Tile) --(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth tileHeight:(CGFloat)tileHeight column:(NSUInteger)column row:(NSUInteger)row; + +-(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth + tileHeight:(CGFloat)tileHeight + column:(NSUInteger)column + row:(NSUInteger)row; + +// Same as previous, but if rigid = YES - adds black pixels to fill +// all tileWidthxtileHeight zone, if there's no enough pixels in image +-(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth + tileHeight:(CGFloat)tileHeight + column:(NSUInteger)column + row:(NSUInteger)row + rigidSize:(BOOL) rigid; + +/** Designated method. + * + * @param potSize If YES - tile width & height will be rounded to nearest POT value, larger then existing. + * potSize have no effect if rigidSize is YES. + * + */ +-(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth + tileHeight:(CGFloat)tileHeight + column:(NSUInteger)column + row:(NSUInteger)row + rigidSize:(BOOL) rigid + POTSize:(BOOL) potSize; + -(NSUInteger)columnsWithTileWidth:(CGFloat)tileWidth; -(NSUInteger)rowsWithTileHeight:(CGFloat)tileHeight; diff --git a/NSBitmapImageRep-Tile.m b/NSBitmapImageRep-Tile.m index 11fc530..3406d19 100644 --- a/NSBitmapImageRep-Tile.m +++ b/NSBitmapImageRep-Tile.m @@ -10,7 +10,29 @@ @implementation NSBitmapImageRep(Tile) --(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth tileHeight:(CGFloat)tileHeight column:(NSUInteger)column row:(NSUInteger)row +-(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth + tileHeight:(CGFloat)tileHeight + column:(NSUInteger)column + row:(NSUInteger)row +{ + return [self subImageWithTileWidth:tileWidth tileHeight:tileHeight column: column row:row rigidSize: NO POTSize: NO]; +} + +-(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth + tileHeight:(CGFloat)tileHeight + column:(NSUInteger)column + row:(NSUInteger)row + rigidSize:(BOOL) rigid +{ + return [self subImageWithTileWidth:tileWidth tileHeight:tileHeight column: column row:row rigidSize: NO POTSize: NO]; +} + +-(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth + tileHeight:(CGFloat)tileHeight + column:(NSUInteger)column + row:(NSUInteger)row + rigidSize:(BOOL) rigid + POTSize:(BOOL) potSize { int width = [self pixelsWide]; int height = [self pixelsHigh]; @@ -25,10 +47,13 @@ -(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth tileHeight:(CGFloat)tileHei int lastCol; int outputWidth; - if (theCol + tileWidth > width) // last column, not full size + if ( !rigid && (theCol + tileWidth > width) ) // last column, not full size { lastCol = width; outputWidth = (width - theCol); + + if (potSize) + outputWidth = (int)pow( 2, ceil(log2((double)outputWidth))); } else { @@ -37,11 +62,13 @@ -(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth tileHeight:(CGFloat)tileHei } int lastRow, outputHeight; - if (theRow + tileHeight > height) + if ( !rigid && (theRow + tileHeight > height) ) { lastRow = height; outputHeight = (height - theRow); - + + if (potSize) + outputHeight = (int)pow( 2, ceil(log2((double)outputHeight))); } else { @@ -74,8 +101,19 @@ -(NSImage *)subImageWithTileWidth:(CGFloat)tileWidth tileHeight:(CGFloat)tileHei { p1 = srcData + bytesPerPixel * (y * width + x); p2 = destData + bytesPerPixel * ((y - theRow) * width + (x - theCol)); - for (i = 0; i < bytesPerPixel; i++) - p2[i] = p1[i]; + + if ( x >= width || y >= height) + { + // fill with zeroes pixels outside self.size + for (i = 0; i < bytesPerPixel; i++) + p2[i] = 0; + } + else + { + // copy pixels as usual + for (i = 0; i < bytesPerPixel; i++) + p2[i] = p1[i]; + } } } diff --git a/README b/README deleted file mode 100644 index 4814bf5..0000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -Tile Cutter is an open source under the non-viral MIT license. Tile Cutter will take a large image and split it up into smaller tiles. I created this for use in a CATiledLayer-backed view for an iPad application, but it could be used for a number of other purposes. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3512fb9 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +Tile-Cutter (by Jeff LaMarche) +================= + +Tile Cutter is an open source under the non-viral MIT license. +Tile Cutter will take a large image and split it up into smaller tiles. I created this for use in a CATiledLayer-backed view for an iPad application, but it could be used for a number of other purposes. + + +This Fork (by Stepan Generalov) +================ + +In this fork of Tile Cutter there was added some things: + +1. Plist file, that have info about tiles position and source image size. +2. Skipping of absolutely transparent tiles +3. Command Line Tool +4. Rigid Tile Size (can be useful if you need fixed tile size, even if image don't fit) + + +There's some minor issues in this fork, that i probably will not fix, cause everything is working fine for me now: + +1. You can't choose other than PNG file type in commandLine tool. +2. You can't set suffix in GUI. +3. There's no progressBar in commandLine tool. Here's a link, that maybe can help you with implementing non-duplicating progress bar: http://en.wikipedia.org/wiki/ANSI_escape_code +4. rigidTilesSize option is available only in commandLine tool. (It allows you to have all tiles with equal size with black pixels that is out of image area) + +If you're using Tile-Cutter and experiencing some issues - feel free to open new Issues or Pull Requests in this Repo. + + diff --git a/Tile Cutter.xcodeproj/project.pbxproj b/Tile Cutter.xcodeproj/project.pbxproj index e1c5075..98fe7c9 100644 --- a/Tile Cutter.xcodeproj/project.pbxproj +++ b/Tile Cutter.xcodeproj/project.pbxproj @@ -6,6 +6,21 @@ objectVersion = 45; objects = { +/* Begin PBXAggregateTarget section */ + 6DA78D271369F7D9000EA884 /* Build All Targets */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 6DA78D3E1369F7FE000EA884 /* Build configuration list for PBXAggregateTarget "Build All Targets" */; + buildPhases = ( + ); + dependencies = ( + 6DA78D2B1369F7E0000EA884 /* PBXTargetDependency */, + 6DA78D2D1369F7E0000EA884 /* PBXTargetDependency */, + ); + name = "Build All Targets"; + productName = "Build All Targets"; + }; +/* End PBXAggregateTarget section */ + /* Begin PBXBuildFile section */ 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; 1F15EAF9125E8ED200B32D97 /* NSImage-Tile.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F15EA19125E826600B32D97 /* NSImage-Tile.m */; }; @@ -17,11 +32,35 @@ 1FFB9979127C874900BE5A73 /* NSInvocation-MCUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FFB9978127C874900BE5A73 /* NSInvocation-MCUtilities.m */; }; 1FFB9A18127CAD9F00BE5A73 /* NSBitmapImageRep-Tile.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FFB9A17127CAD9F00BE5A73 /* NSBitmapImageRep-Tile.m */; }; 256AC3DA0F4B6AC300CF3369 /* Tile_CutterAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* Tile_CutterAppDelegate.m */; }; + 6DA78C0D1369BEE1000EA884 /* cmdToolMain.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DA78C0C1369BEE1000EA884 /* cmdToolMain.m */; }; + 6DA78C441369C5FE000EA884 /* TileCutterCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DA78C421369C5FE000EA884 /* TileCutterCore.m */; }; + 6DA78CD11369E71D000EA884 /* NSInvocation-MCUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FFB9978127C874900BE5A73 /* NSInvocation-MCUtilities.m */; }; + 6DA78CD21369E71D000EA884 /* NSImage-Tile.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F15EA19125E826600B32D97 /* NSImage-Tile.m */; }; + 6DA78CD41369E71D000EA884 /* NSBitmapImageRep-Tile.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FFB9A17127CAD9F00BE5A73 /* NSBitmapImageRep-Tile.m */; }; + 6DA78CD51369E71D000EA884 /* TileCutterCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DA78C421369C5FE000EA884 /* TileCutterCore.m */; }; + 6DA78CE11369E737000EA884 /* TileOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FFB9940127C847100BE5A73 /* TileOperation.m */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 6DA78D2A1369F7E0000EA884 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D1107260486CEB800E47090; + remoteInfo = "Tile Cutter"; + }; + 6DA78D2C1369F7E0000EA884 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6DA78C061369BEB0000EA884; + remoteInfo = tileCutter; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; @@ -47,11 +86,23 @@ 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 6DA78C071369BEB0000EA884 /* tileCutter */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tileCutter; sourceTree = BUILT_PRODUCTS_DIR; }; + 6DA78C0C1369BEE1000EA884 /* cmdToolMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cmdToolMain.m; sourceTree = ""; }; + 6DA78C411369C5FE000EA884 /* TileCutterCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TileCutterCore.h; sourceTree = ""; }; + 6DA78C421369C5FE000EA884 /* TileCutterCore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TileCutterCore.m; sourceTree = ""; }; + 6DA78CB51369E61A000EA884 /* TileCutterSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TileCutterSettings.h; sourceTree = ""; }; 8D1107310486CEB800E47090 /* Tile_Cutter-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Tile_Cutter-Info.plist"; sourceTree = ""; }; 8D1107320486CEB800E47090 /* Tile Cutter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Tile Cutter.app"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 6DA78C051369BEB0000EA884 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8D11072E0486CEB800E47090 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -66,6 +117,7 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( + 6DA78CB51369E61A000EA884 /* TileCutterSettings.h */, 1FFB9977127C874900BE5A73 /* NSInvocation-MCUtilities.h */, 1FFB9978127C874900BE5A73 /* NSInvocation-MCUtilities.m */, 256AC3D80F4B6AC300CF3369 /* Tile_CutterAppDelegate.h */, @@ -80,6 +132,8 @@ 1FFB9940127C847100BE5A73 /* TileOperation.m */, 1FFB9A16127CAD9F00BE5A73 /* NSBitmapImageRep-Tile.h */, 1FFB9A17127CAD9F00BE5A73 /* NSBitmapImageRep-Tile.m */, + 6DA78C411369C5FE000EA884 /* TileCutterCore.h */, + 6DA78C421369C5FE000EA884 /* TileCutterCore.m */, ); name = Classes; sourceTree = ""; @@ -106,6 +160,7 @@ isa = PBXGroup; children = ( 8D1107320486CEB800E47090 /* Tile Cutter.app */, + 6DA78C071369BEB0000EA884 /* tileCutter */, ); name = Products; sourceTree = ""; @@ -127,6 +182,7 @@ children = ( 256AC3F00F4B6AF500CF3369 /* Tile_Cutter_Prefix.pch */, 29B97316FDCFA39411CA2CEA /* main.m */, + 6DA78C0C1369BEE1000EA884 /* cmdToolMain.m */, ); name = "Other Sources"; sourceTree = ""; @@ -155,6 +211,22 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 6DA78C061369BEB0000EA884 /* tileCutter */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6DA78C0B1369BECE000EA884 /* Build configuration list for PBXNativeTarget "tileCutter" */; + buildPhases = ( + 6DA78C041369BEB0000EA884 /* Sources */, + 6DA78C051369BEB0000EA884 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = tileCutter; + productName = tileCutter; + productReference = 6DA78C071369BEB0000EA884 /* tileCutter */; + productType = "com.apple.product-type.tool"; + }; 8D1107260486CEB800E47090 /* Tile Cutter */ = { isa = PBXNativeTarget; buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Tile Cutter" */; @@ -193,6 +265,8 @@ projectRoot = ""; targets = ( 8D1107260486CEB800E47090 /* Tile Cutter */, + 6DA78C061369BEB0000EA884 /* tileCutter */, + 6DA78D271369F7D9000EA884 /* Build All Targets */, ); }; /* End PBXProject section */ @@ -212,6 +286,19 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 6DA78C041369BEB0000EA884 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6DA78C0D1369BEE1000EA884 /* cmdToolMain.m in Sources */, + 6DA78CD11369E71D000EA884 /* NSInvocation-MCUtilities.m in Sources */, + 6DA78CD21369E71D000EA884 /* NSImage-Tile.m in Sources */, + 6DA78CD41369E71D000EA884 /* NSBitmapImageRep-Tile.m in Sources */, + 6DA78CD51369E71D000EA884 /* TileCutterCore.m in Sources */, + 6DA78CE11369E737000EA884 /* TileOperation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8D11072C0486CEB800E47090 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -224,11 +311,25 @@ 1FFB9941127C847100BE5A73 /* TileOperation.m in Sources */, 1FFB9979127C874900BE5A73 /* NSInvocation-MCUtilities.m in Sources */, 1FFB9A18127CAD9F00BE5A73 /* NSBitmapImageRep-Tile.m in Sources */, + 6DA78C441369C5FE000EA884 /* TileCutterCore.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 6DA78D2B1369F7E0000EA884 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D1107260486CEB800E47090 /* Tile Cutter */; + targetProxy = 6DA78D2A1369F7E0000EA884 /* PBXContainerItemProxy */; + }; + 6DA78D2D1369F7E0000EA884 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 6DA78C061369BEB0000EA884 /* tileCutter */; + targetProxy = 6DA78D2C1369F7E0000EA884 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; @@ -249,6 +350,73 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 6DA78C091369BEB0000EA884 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = tileCutter; + }; + name = Debug; + }; + 6DA78C0A1369BEB0000EA884 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = tileCutter; + ZERO_LINK = NO; + }; + name = Release; + }; + 6DA78D281369F7DA000EA884 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = "Build All Targets"; + }; + name = Debug; + }; + 6DA78D291369F7DA000EA884 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = "Build All Targets"; + ZERO_LINK = NO; + }; + name = Release; + }; C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -326,6 +494,24 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 6DA78C0B1369BECE000EA884 /* Build configuration list for PBXNativeTarget "tileCutter" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6DA78C091369BEB0000EA884 /* Debug */, + 6DA78C0A1369BEB0000EA884 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6DA78D3E1369F7FE000EA884 /* Build configuration list for PBXAggregateTarget "Build All Targets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6DA78D281369F7DA000EA884 /* Debug */, + 6DA78D291369F7DA000EA884 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Tile Cutter" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/TileCutterCore.h b/TileCutterCore.h new file mode 100644 index 0000000..0072b86 --- /dev/null +++ b/TileCutterCore.h @@ -0,0 +1,52 @@ +// +// TileCutterCore.h +// Tile Cutter +// +// Created by Stepan Generalov on 28.04.11. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import +#import "TileOperation.h" + + +@interface TileCutterCore : NSObject +{} + +#pragma mark Public Properties + +// Properties of Tiling Operation +@property(readwrite) BOOL POTTiles; +@property(readwrite) BOOL rigidTiles; +@property(readwrite) BOOL keepAllTiles; +@property(readwrite) NSUInteger tileWidth; +@property(readwrite) NSUInteger tileHeight; +@property(readwrite) float_t contentScaleFactor; +@property(readwrite) TileCutterOutputPrefs outputFormat; +@property(readwrite, copy) NSString *inputFilename; +@property(readwrite, copy) NSString *outputBaseFilename; +@property(readwrite, copy) NSString *outputSuffix; + +// Properties of Global Tiling Operation Status +@property(readwrite) int tileRowCount; +@property(readwrite) int tileColCount; +@property(readwrite) int progressCol; +@property(readwrite) int progressRow; + +// TileOperationDelegate messages will be forwarded after processing to this delegate +@property(readwrite, assign) NSObject *operationsDelegate; + +#pragma mark Public Methods + +- (void) startSavingTiles; + +#pragma mark Private Properties + +// Queue of Operations - used internally +@property(nonatomic, retain) NSOperationQueue *queue; + +// Info, prepared for output.plist - used internally +@property(readwrite, retain) NSArray *allTilesInfo; +@property(readwrite, retain) NSDictionary *imageInfo; + +@end diff --git a/TileCutterCore.m b/TileCutterCore.m new file mode 100644 index 0000000..748c8c0 --- /dev/null +++ b/TileCutterCore.m @@ -0,0 +1,188 @@ +// +// TileCutterCore.m +// Tile Cutter +// +// Created by Stepan Generalov on 28.04.11. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import "TileCutterCore.h" +#import "NSImage-Tile.h" + +@implementation TileCutterCore + +@synthesize keepAllTiles, tileWidth, tileHeight, inputFilename, + outputBaseFilename, outputSuffix, operationsDelegate, + queue, allTilesInfo, imageInfo, outputFormat; +@synthesize rigidTiles; +@synthesize contentScaleFactor; +@synthesize POTTiles; + +#pragma mark Public Methods + +- (id) init +{ + if ( (self == [super init]) ) + { + self.queue = [[[NSOperationQueue alloc] init] autorelease]; + [self.queue setMaxConcurrentOperationCount:1]; + + self.outputFormat = NSPNGFileType; + self.outputSuffix = @""; + self.keepAllTiles = NO; + self.rigidTiles = NO; + } + + return self; +} + +- (void) dealloc +{ + self.queue = nil; + self.allTilesInfo = nil; + self.imageInfo = nil; + self.inputFilename = nil; + self.outputBaseFilename = nil; + self.outputSuffix = nil; + + [super dealloc]; +} + +- (void) startSavingTiles +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSImage *image = [[[NSImage alloc] initWithContentsOfFile: self.inputFilename] autorelease]; + + progressCol = 0; + progressRow = 0; + + tileRowCount = [image rowsWithTileHeight: self.tileHeight]; + tileColCount = [image columnsWithTileWidth: self.tileWidth]; + + NSSize outputImageSizeForPlist = [image size]; + outputImageSizeForPlist.width /= self.contentScaleFactor; + outputImageSizeForPlist.height /= self.contentScaleFactor; + self.imageInfo = [NSDictionary dictionaryWithObjectsAndKeys: + [self.inputFilename lastPathComponent], @"Filename", + NSStringFromSize(outputImageSizeForPlist), @"Size", nil]; + + self.allTilesInfo = [NSMutableArray arrayWithCapacity: tileRowCount * tileColCount]; + + // One ImageRep for all TileOperation + NSBitmapImageRep *imageRep = + [[[NSBitmapImageRep alloc] initWithCGImage:[image CGImageForProposedRect:NULL context:NULL hints:nil]] autorelease]; + + for (int row = 0; row < tileRowCount; row++) + { + TileOperation *op = [[TileOperation alloc] init]; + op.row = row; + op.tileWidth = self.tileWidth; + op.tileHeight = self.tileHeight; + op.imageRep = imageRep; + op.baseFilename = outputBaseFilename; + op.delegate = self; + op.outputFormat = self.outputFormat; + op.outputSuffix = self.outputSuffix; + op.skipTransparentTiles = (! self.keepAllTiles ); + op.rigidTiles = self.rigidTiles; + op.POTTiles = self.POTTiles; + [queue addOperation:op]; + [op release]; + } + + [pool drain]; +} + +- (void)operationDidFinishTile:(TileOperation *)op +{ + progressCol++; + if (progressCol >= tileColCount) + { + progressCol = 0; + progressRow++; + } + + if ([self.operationsDelegate respondsToSelector: _cmd]) + [self.operationsDelegate performSelectorOnMainThread: _cmd + withObject: op + waitUntilDone: NO]; +} + +- (void) saveImageInfoDictionary +{ + // Change coordinates & size of all tiles for contentScaleFactor + if (self.contentScaleFactor != 1.0f) + { + // Create new array, that will replace old self.allTilesInfo + NSMutableArray *newTilesInfoArray = [NSMutableArray arrayWithCapacity: [self.allTilesInfo count]]; + + for (NSDictionary *tileDict in self.allTilesInfo) + { + // Get Tile Rect + NSRect rect = NSRectFromString([tileDict objectForKey: @"Rect"]); + + // Divide it by contentScaleFactor + rect.origin.x /= self.contentScaleFactor; + rect.origin.y /= self.contentScaleFactor; + rect.size.width /= self.contentScaleFactor; + rect.size.height /= self.contentScaleFactor; + + // Create new tile info Dict with changed rect + NSDictionary *newTileDict = [NSDictionary dictionaryWithObjectsAndKeys: + [tileDict objectForKey:@"Name"], @"Name", + NSStringFromRect(rect), @"Rect", + nil]; + + // Add new tile info for new array + [newTilesInfoArray addObject:newTileDict ]; + + } + + // Replace Old Tiles with New + self.allTilesInfo = newTilesInfoArray; + + } + + // Create Root Dictionary for a PLIST file + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + self.imageInfo, @"Source", + self.allTilesInfo, @"Tiles", + [NSNumber numberWithFloat:self.contentScaleFactor], @"ContentScaleFactor", nil]; + + // Be safe with outputSuffix + if (!self.outputSuffix) + self.outputSuffix = @""; + + // Save Dict to File + [dict writeToFile:[NSString stringWithFormat:@"%@%@.plist", self.outputBaseFilename, self.outputSuffix] atomically:YES]; +} + +- (void)operationDidFinishSuccessfully:(TileOperation *)op +{ + [(NSMutableArray *)self.allTilesInfo addObjectsFromArray: op.tilesInfo]; + op.tilesInfo = nil; + + // All Tiles Finished? + if (progressRow >= tileRowCount) + { + [self saveImageInfoDictionary]; + } + + if ([self.operationsDelegate respondsToSelector: _cmd]) + [self.operationsDelegate performSelectorOnMainThread: _cmd + withObject: op + waitUntilDone: NO]; +} + + +- (void)operation:(TileOperation *)op didFailWithMessage:(NSString *)message +{ + if ([self.operationsDelegate respondsToSelector: _cmd]) + [self.operationsDelegate performSelectorOnMainThread: _cmd + withObject: op + waitUntilDone: NO]; +} + + +@end diff --git a/TileCutterSettings.h b/TileCutterSettings.h new file mode 100644 index 0000000..0dbbcf5 --- /dev/null +++ b/TileCutterSettings.h @@ -0,0 +1,19 @@ +// +// TileCutterSettings.h +// Tile Cutter +// +// Created by Stepan Generalov on 28.04.11. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import + +typedef enum +{ + TileCutterOutputPrefsJPEG, + TileCutterOutputPrefsGIF, + TileCutterOutputPrefsTIFF, + TileCutterOutputPrefsBMP, + TileCutterOutputPrefsPNG, + TileCutterOutputPrefsJPEG2000 +} TileCutterOutputPrefs; \ No newline at end of file diff --git a/TileCutterView.h b/TileCutterView.h index 4264348..14a7202 100644 --- a/TileCutterView.h +++ b/TileCutterView.h @@ -19,6 +19,7 @@ @property (nonatomic, retain) IBOutlet NSColorWell *guideColorWell; @property (nonatomic, retain) NSImage *image; @property (nonatomic, retain) IBOutlet NSButton *guideCheckbox; +@property (nonatomic, retain) IBOutlet NSButton *skipCheckbox; @property (nonatomic, retain) IBOutlet NSButton *saveButton; - (IBAction) valueChanged:(id)sender; @end diff --git a/TileCutterView.m b/TileCutterView.m index a9f827b..65e0300 100644 --- a/TileCutterView.m +++ b/TileCutterView.m @@ -11,6 +11,7 @@ @implementation TileCutterView @synthesize filename, tileWidthField, tileHeightField, guideColorWell, image, guideCheckbox, saveButton; +@synthesize skipCheckbox; - (id)initWithFrame:(NSRect)frame { diff --git a/TileOperation.h b/TileOperation.h index 611c612..213886d 100644 --- a/TileOperation.h +++ b/TileOperation.h @@ -7,6 +7,7 @@ // #import +#import "TileCutterSettings.h" @class TileOperation; @@ -20,13 +21,26 @@ @interface TileOperation : NSOperation { - + } @property (assign) NSObject *delegate; @property (retain) NSBitmapImageRep *imageRep; @property NSUInteger row; @property (retain) NSString *baseFilename; +@property (readwrite, copy) NSString *outputSuffix; @property NSUInteger tileHeight; @property NSUInteger tileWidth; @property TileCutterOutputPrefs outputFormat; +@property (readwrite) BOOL skipTransparentTiles; +@property (readwrite) BOOL rigidTiles; +@property (readwrite) BOOL POTTiles; + +/* array of tiles info, + * each item = NSDictionary { + * NSString *name - output filename of tile + * NSString from CGRect of that tile in source Image + */ +@property (readwrite, retain)NSMutableArray *tilesInfo; + + @end diff --git a/TileOperation.m b/TileOperation.m index 9110351..67c58f4 100644 --- a/TileOperation.m +++ b/TileOperation.m @@ -11,8 +11,41 @@ #import "NSInvocation-MCUtilities.h" #import "NSBitmapImageRep-Tile.h" +@interface NSBitmapImageRep (Extension) + +- (BOOL) isAbsoluteTransparent; + +@end + +@implementation NSBitmapImageRep (Extension) + +- (BOOL) isAbsoluteTransparent +{ + NSSize s = [self size]; + + for (int i = 0; i < s.width; ++i) + { + for (int j = 0; j < s.height; ++j) + { + NSColor *col = [self colorAtX:i y:j]; + + if ([col alphaComponent] != 0.0f) + return NO; + } + } + + return YES; +} + +@end + + @implementation TileOperation @synthesize delegate, imageRep, row, baseFilename, tileHeight, tileWidth, outputFormat; +@synthesize tilesInfo; +@synthesize skipTransparentTiles; +@synthesize outputSuffix; +@synthesize rigidTiles, POTTiles; #pragma mark - - (void)informDelegateOfError:(NSString *)message { @@ -65,12 +98,25 @@ - (void)main extension = @"jpg"; fileType = NSJPEGFileType; break; - } + } + // Get Tile Count for this Operation int tileColCount = [imageRep columnsWithTileWidth:tileWidth]; + + // Create tilesInfo Array for holding this Operation Tiles Info + self.tilesInfo = [NSMutableArray arrayWithCapacity: tileColCount]; + + // Safe Empty Suffix + if (!self.outputSuffix) + self.outputSuffix = @""; + for (int column = 0; column < tileColCount; column++) { - NSImage *subImage = [imageRep subImageWithTileWidth:(float)tileWidth tileHeight:(float)tileHeight column:column row:row]; + NSImage *subImage = [imageRep subImageWithTileWidth:(float)tileWidth + tileHeight:(float)tileHeight + column:column + row:row + rigidSize:self.rigidTiles]; if (subImage == nil) { @@ -78,38 +124,59 @@ - (void)main goto finish; } - NSArray * representations = [subImage representations]; - - if ([self isCancelled]) - goto finish; - - NSData *bitmapData = [NSBitmapImageRep representationOfImageRepsInArray:representations - usingType:fileType properties:nil]; - - if (bitmapData == nil) - { - [self informDelegateOfError:NSLocalizedString(@"Error retrieving bitmap data from result", @"")]; - goto finish; - } - - + NSArray * representations = [subImage representations]; + NSBitmapImageRep *subBitmapRep = nil; + if ( [representations count] ) + subBitmapRep = [representations objectAtIndex: 0]; + if ([self isCancelled]) goto finish; - NSString *outPath = [NSString stringWithFormat:@"%@_%d_%d.%@", baseFilename, row, column, extension]; - [bitmapData writeToFile:outPath atomically:YES]; + + + // Analyze do we need this tile saved + BOOL curTileNeeded = ! (self.skipTransparentTiles && [subBitmapRep isAbsoluteTransparent]); + + // save if yes + if ( curTileNeeded ) + { + NSData *bitmapData = [NSBitmapImageRep representationOfImageRepsInArray:representations + usingType:fileType properties:nil]; + + + if (bitmapData == nil) + { + [self informDelegateOfError:NSLocalizedString(@"Error retrieving bitmap data from result", @"")]; + goto finish; + } + + + if ([self isCancelled]) + goto finish; + + NSString *outPath = [NSString stringWithFormat:@"%@_%d_%d%@.%@", baseFilename, row, column, self.outputSuffix, extension]; + [bitmapData writeToFile:outPath atomically:YES]; + + // Add created Tile Info to tilesInfo array + NSRect tileRect = NSRectFromCGRect( CGRectMake(column * tileWidth, + row * tileHeight, + [subImage size].width, + [subImage size].height)); + NSDictionary *tileInfoDict = [NSDictionary dictionaryWithObjectsAndKeys: + [outPath lastPathComponent], @"Name", + NSStringFromRect(tileRect), @"Rect", + nil]; + [(NSMutableArray *)self.tilesInfo addObject: tileInfoDict ]; + } if ([delegate respondsToSelector:@selector(operationDidFinishTile:)]) - [delegate performSelectorOnMainThread:@selector(operationDidFinishTile:) - withObject:self - waitUntilDone:NO]; + [delegate operationDidFinishTile: self]; } if ([delegate respondsToSelector:@selector(operationDidFinishSuccessfully:)]) - [delegate performSelectorOnMainThread:@selector(operationDidFinishSuccessfully:) - withObject:self - waitUntilDone:NO]; + [delegate operationDidFinishSuccessfully: self ]; + finish: [pool drain]; } @@ -121,9 +188,11 @@ - (void)main - (void)dealloc { + self.outputSuffix = nil; delegate = nil; [imageRep release], imageRep = nil; [baseFilename release], baseFilename = nil; + self.tilesInfo = nil; [super dealloc]; } diff --git a/Tile_CutterAppDelegate.h b/Tile_CutterAppDelegate.h index 596a057..e54af56 100644 --- a/Tile_CutterAppDelegate.h +++ b/Tile_CutterAppDelegate.h @@ -7,7 +7,7 @@ // #import -#import "TileOperation.h" +#import "TileCutterCore.h" @class TileCutterView; @interface Tile_CutterAppDelegate : NSObject @@ -23,7 +23,8 @@ @property (retain) IBOutlet NSWindow *progressWindow; @property (retain) IBOutlet NSTextField *progressLabel; @property (retain) IBOutlet NSString *baseFilename; -@property (nonatomic, retain) NSOperationQueue *queue; + +@property (retain) TileCutterCore *tileCore; - (IBAction)saveButtonPressed:(id)sender; - (IBAction)openSelected:(id)sender; diff --git a/Tile_CutterAppDelegate.m b/Tile_CutterAppDelegate.m index f901c2a..ca48cb3 100644 --- a/Tile_CutterAppDelegate.m +++ b/Tile_CutterAppDelegate.m @@ -16,18 +16,15 @@ @interface Tile_CutterAppDelegate() -{ - int tileHeight, tileWidth; - int tileRowCount, tileColCount; - int progressCol, progressRow; -} + - (void)delayAlert:(NSString *)message; + @end @implementation Tile_CutterAppDelegate -@synthesize window, tileCutterView, widthTextField, heightTextField, rowBar, columnBar, progressWindow, progressLabel, baseFilename, queue; +@synthesize window, tileCutterView, widthTextField, heightTextField, rowBar, columnBar, progressWindow, progressLabel, baseFilename; - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename { @@ -55,50 +52,31 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification [defaults setInteger:200 forKey:@"heightField"]; } - self.queue = [[[NSOperationQueue alloc] init] autorelease]; + self.tileCore = [TileCutterCore new]; + self.tileCore.operationsDelegate = self; } + - (void)saveThread { NSLog(@"Save thread started"); NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSImage *image = [[[NSImage alloc] initWithContentsOfFile:tileCutterView.filename] autorelease]; - - [rowBar setIndeterminate:NO]; - [columnBar setIndeterminate:NO]; - [rowBar setMaxValue:(double)[image rowsWithTileHeight:[heightTextField floatValue]]]; - [rowBar setMinValue:0.]; - [rowBar setDoubleValue:0.]; - [columnBar setMinValue:0.]; - [columnBar setMaxValue:(double)[image columnsWithTileWidth:[widthTextField floatValue]]]; - [columnBar setDoubleValue:0.]; - - progressCol = 0; - progressRow = 0; - - tileRowCount = [image rowsWithTileHeight:tileHeight]; - tileColCount = [image columnsWithTileWidth:tileWidth]; - - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - TileCutterOutputPrefs outputFormat = (TileCutterOutputPrefs)[defaults integerForKey:@"OutputFormat"]; - for (int row = 0; row < tileRowCount; row++) - { - // Each row operation gets its own ImageRep to avoid contention - NSBitmapImageRep *imageRep = [[[NSBitmapImageRep alloc] initWithCGImage:[image CGImageForProposedRect:NULL context:NULL hints:nil]] autorelease]; - TileOperation *op = [[TileOperation alloc] init]; - op.row = row; - op.tileWidth = tileWidth; - op.tileHeight = tileHeight; - op.imageRep = imageRep; - op.baseFilename = baseFilename; - op.delegate = self; - op.outputFormat = outputFormat; - [queue addOperation:op]; - [op release]; - } + + // Setup Tile Core + self.tileCore.inputFilename = tileCutterView.filename; + self.tileCore.outputFormat = (TileCutterOutputPrefs)[[NSUserDefaults standardUserDefaults] integerForKey:@"OutputFormat"]; + self.tileCore.outputBaseFilename = baseFilename; + if (![tileCutterView.skipCheckbox intValue]) + self.tileCore.keepAllTiles = YES; + else + self.tileCore.keepAllTiles = NO; + + // Start Tiling + [self.tileCore startSavingTiles]; [pool drain]; } + - (IBAction)saveButtonPressed:(id)sender { NSSavePanel *sp = [NSSavePanel savePanel]; @@ -117,8 +95,8 @@ -(void)didEndSaveSheet:(NSSavePanel *)savePanel if (returnCode == NSOKButton) { self.baseFilename = [[savePanel filename] stringByDeletingPathExtension]; - tileHeight = [heightTextField intValue]; - tileWidth = [widthTextField intValue]; + self.tileCore.tileHeight = [heightTextField intValue]; + self.tileCore.tileWidth = [widthTextField intValue]; [self performSelector:@selector(delayPresentSheet) withObject:nil afterDelay:0.1]; } @@ -140,6 +118,7 @@ - (void)delayPresentSheet //[queue setSuspended:YES]; [self performSelectorInBackground:@selector(saveThread) withObject:nil]; } + - (void)didEndSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { [sheet orderOut:self]; @@ -161,33 +140,29 @@ - (IBAction)openSelected:(id)sender } - (void)dealloc { + self.tileCore = nil; [columnBar release], columnBar = nil; [rowBar release], rowBar = nil; [progressWindow release], progressWindow = nil; [progressLabel release], progressLabel = nil; [baseFilename release], baseFilename = nil; - [queue release], queue = nil; [super dealloc]; } #pragma mark - - (void)updateProgress { - if (progressRow >= tileRowCount) + if (self.tileCore.progressRow >= self.tileCore.tileRowCount) + { [NSApp endSheet:progressWindow]; + } // [rowBar setDoubleValue:(double)progressRow]; // [columnBar setDoubleValue:(double)progressCol]; - [progressLabel setStringValue:[NSString stringWithFormat:@"Processing row %d, column %d", progressRow, progressCol]]; + [progressLabel setStringValue:[NSString stringWithFormat:@"Processing row %d, column %d", self.tileCore.progressRow, self.tileCore.progressCol]]; } - (void)operationDidFinishTile:(TileOperation *)op { - progressCol++; - if (progressCol >= tileColCount) - { - progressCol = 0; - progressRow++; - } - if (progressRow >= tileRowCount) + if (self.tileCore.progressRow >= self.tileCore.tileRowCount) [NSApp endSheet:progressWindow]; // [rowBar setDoubleValue:(double)progressRow]; @@ -196,6 +171,12 @@ - (void)operationDidFinishTile:(TileOperation *)op [self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO]; } + +- (void)operationDidFinishSuccessfully:(TileOperation *)op +{ + +} + - (void)delayAlert:(NSString *)message { NSAlert *alert = [[[NSAlert alloc] init] autorelease]; @@ -205,6 +186,7 @@ - (void)delayAlert:(NSString *)message [alert setAlertStyle:NSCriticalAlertStyle]; [alert beginSheetModalForWindow:[self window] modalDelegate:nil didEndSelector:nil contextInfo:nil]; } + - (void)operation:(TileOperation *)op didFailWithMessage:(NSString *)message { [NSApp endSheet:progressWindow]; diff --git a/Tile_Cutter_Prefix.pch b/Tile_Cutter_Prefix.pch index 21d7b6c..b4fbdc8 100644 --- a/Tile_Cutter_Prefix.pch +++ b/Tile_Cutter_Prefix.pch @@ -4,15 +4,4 @@ #ifdef __OBJC__ #import -#endif - - -typedef enum -{ - TileCutterOutputPrefsJPEG, - TileCutterOutputPrefsGIF, - TileCutterOutputPrefsTIFF, - TileCutterOutputPrefsBMP, - TileCutterOutputPrefsPNG, - TileCutterOutputPrefsJPEG2000 -} TileCutterOutputPrefs; \ No newline at end of file +#endif \ No newline at end of file diff --git a/cmdToolMain.m b/cmdToolMain.m new file mode 100644 index 0000000..1725ef1 --- /dev/null +++ b/cmdToolMain.m @@ -0,0 +1,207 @@ +// +// cmdToolMain.m +// Tile Cutter +// +// Created by Stepan Generalov on 28.04.11. +// Copyright 2011 __MyCompanyName__. All rights reserved. +// + +#import + +#import +#import +#import + +#import "TileCutterCore.h" +#import "TileCutterSettings.h" + +void printUsageAndExit() +{ + printf("\nUsage: tileCutter [--rigidTilesSize | --roundTileSizeToPOT] [--keepTransparentTiles] --tileWidth WIDTH --tileHeight HEIGHT \ +--inputFile INPUT.PNG --outputFile OUTPUT [--outputSuffix SUFFIX]\n\ +HEIGHT & WIDTH should be >= 1\n\ +output tiles file names will be in this format:\n\ +OUTPUT_X_Y-SUFFIX.png,\n\ +where X & Y is tile number\n\ +Output plist file will be: OUTPUT.plist\n\ +If --rigidTilesSize flag is set, then all tiles will have the same size.\n\ +If --roundTileSizeToPOT flag is set, then all tiles will have POT size, and rigidTileSize will be disabled.\n\ +If image isn't divisible by tileSize without a remainder - missing pixels will be added.\n\ +If --keepTransparentTiles flag is set, than absolute transparent tiles will be not skiped.\n\ +--contentScaleFactor is used as denominator for tiles & image sizes & positions in plist file.\n"); + exit(0); +} + +BOOL fileExists(NSString* relPath) +{ + NSString *fullpath = nil; + + // only if it is not an absolute path + if( ! [relPath isAbsolutePath] ) + { + NSString *file = [relPath lastPathComponent]; + NSString *imageDirectory = [relPath stringByDeletingLastPathComponent]; + + fullpath = [[NSBundle mainBundle] pathForResource:file + ofType:nil + inDirectory:imageDirectory]; + } + + if (fullpath == nil) + fullpath = relPath; + + return [[NSFileManager defaultManager] fileExistsAtPath: fullpath]; +} + +int main(int argc, char *argv[]) +{ + // Options Storage + int keepAllTiles = 0; + int rigidTilesSize = 0; + int roundTileSizeToPOT = 0; + NSUInteger tileWidth = 0; + NSUInteger tileHeight = 0; + NSString *inputFilename = nil; + NSString *outputBaseFilename = nil; + NSString *outputSuffix = nil; + float_t contentScaleFactor = 1.0f; + + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + + int c; + + if (argc <= 1) + { + [pool release]; + printUsageAndExit(); + } + + while (1) + { + struct option long_options[] = + { + /* These options set a flag. */ + {"skipTransparentTiles", no_argument, &keepAllTiles, 0}, + {"keepTransparentTiles", no_argument, &keepAllTiles, 1}, + {"rigidTilesSize", no_argument, &rigidTilesSize, 1}, + {"roundTileSizeToPOT", no_argument, &rigidTilesSize, 0}, + /* These options don't set a flag. + We distinguish them by their indices. */ + {"tileWidth", required_argument, 0, 'w'}, + {"tileHeight", required_argument, 0, 'h'}, + {"inputFile", required_argument, 0, 'i'}, + {"outputFile", required_argument, 0, 'o'}, + {"outputSuffix", required_argument, 0, 's'}, + {"contentScaleFactor", required_argument, 0, 'f'}, + {0, 0, 0, 0} + }; + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long (argc, argv, "w:h:i:o:s:", + long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1) + break; + + switch (c) + { + case 0: + /* If this option set a flag, do nothing else now. */ + if (long_options[option_index].flag != 0) + break; + + // if this is unkown option - print usage and exit + [pool release]; + printUsageAndExit(); + + break; + + case 'w': + tileWidth = (NSUInteger)[[NSString stringWithCString: optarg encoding: NSUTF8StringEncoding] intValue]; + if (!tileWidth) + { + [pool release]; + printUsageAndExit(); + } + break; + + case 'h': + tileHeight = (NSUInteger)[[NSString stringWithCString: optarg encoding: NSUTF8StringEncoding] intValue]; + if (!tileHeight) + { + [pool release]; + printUsageAndExit(); + } + break; + + case 'i': + inputFilename = [NSString stringWithCString: optarg encoding: NSUTF8StringEncoding]; + break; + + case 'o': + outputBaseFilename = [NSString stringWithCString:optarg encoding: NSUTF8StringEncoding]; + break; + + case 's': + outputSuffix = [NSString stringWithCString:optarg encoding: NSUTF8StringEncoding]; + break; + + case 'f': + contentScaleFactor = [[NSString stringWithCString:optarg encoding: NSUTF8StringEncoding] floatValue]; + if (!contentScaleFactor) + contentScaleFactor = 1.0f; + break; + + case '?': + /* getopt_long already printed an error message. */ + [pool release]; + printUsageAndExit(); + break; + + default: + abort (); + } + } + + + // We don't accept any nonOptions cmd line arguments + if (optind < argc) + { + [pool release]; + printUsageAndExit(); + } + + // avoid crashing if file not exists + if ( !fileExists(inputFilename) ) + { + printf("\nFile not found: %s \n", [inputFilename cStringUsingEncoding:NSUTF8StringEncoding]); + [pool release]; + exit(0); + } + + // Round To POT must disable rigidTileSize. + if (roundTileSizeToPOT) + rigidTilesSize = 0; + + // Prepare Tile Cutter + TileCutterCore *tileCutterCore = [TileCutterCore new]; + tileCutterCore.inputFilename = inputFilename; + tileCutterCore.outputBaseFilename = outputBaseFilename; + tileCutterCore.outputSuffix = outputSuffix; + tileCutterCore.tileWidth = tileWidth; + tileCutterCore.tileHeight = tileWidth; + tileCutterCore.keepAllTiles = (keepAllTiles != 0); + tileCutterCore.rigidTiles = (rigidTilesSize != 0); + tileCutterCore.POTTiles = (roundTileSizeToPOT != 0); + tileCutterCore.contentScaleFactor = contentScaleFactor; + + // Start Cutting and Wait for it. + [tileCutterCore startSavingTiles]; + [tileCutterCore.queue waitUntilAllOperationsAreFinished]; + + [pool release]; + return 0; +} +