EsoErik

Friday, November 2, 2012

 

iOS 6: Using the Grouped Table Pinstripe Background in Other Views

It took some trial and error to find a way to use the built in methods in iOS 6 to draw the grouped table style pinstriped background in views that are not tables.  In iOS 6, doing self.view.background = [UIColor groupTableViewBackgroundColor] is deprecated and, furthermore, does not work.

Duplicating the iOS 6 table pinstriping by setting a view's background to a custom pattern color would be trivial, but it won't match the style for other iOS versions.  Using Apple's drawing routine guarantees that view backgrounds will always look correct.  I accomplished this by setting my view's background to transparent and then programmatically making an empty grouped table and placing it behind everything else:

// An empty table view useful for putting last in a view's Z-order to draw the grouped table view pinstripe background
@interface S3PersonViewBackground : UITableView<UITableViewDataSource, UITableViewDelegate>
@end


@implementation S3PersonViewBackground

-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
{
    return 0;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 0;
}

@end


//


@interface S3PersonViewController : UIViewController

@property (nonatomic, retain) S3Person* person;

@end


@interface S3PersonViewController ()
{
    // Used only for drawing the grouped table style background
    S3PersonViewBackground* backgroundDrawingView;
}
@end


@implementation S3PersonViewController

-(void)dealloc
{
    [_person release];
    [backgroundDrawingView release];
    [super dealloc];
}

-(void)viewDidLoad
{
    [super viewDidLoad];
    self.title = [NSString stringWithFormat:@"%@ %@", self.person.firstName, self.person.lastName];
    //[self.view setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
    backgroundDrawingView = [[S3PersonViewBackground alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 0.0) style:UITableViewStyleGrouped];
    backgroundDrawingView.dataSource = backgroundDrawingView;
    backgroundDrawingView.delegate = backgroundDrawingView;
    [self.view addSubview:backgroundDrawingView];
    [self.view sendSubviewToBack:backgroundDrawingView];
}

-(void)viewDidLayoutSubviews
{
    backgroundDrawingView.frame = self.view.bounds;
}

@end

Labels: , ,


Thursday, June 23, 2011

 

Using Windows Terminal Blocky Font With Visual Studio 2010

Use this special font at 12 point. I created it following Erik Olofsson's instructions.

Labels: ,


Monday, April 25, 2011

 

Overriding boost::gil's from-RGBA Color Converter

When converting to formats without an alpha channel, the Boost Generic Image Library interprets RGBA images as using premultiplied alpha. An application that I am writing stores non-graphical data in the alpha channel. Converting from such images to formats without an alpha channel requires simply discarding alpha channel data, rather than using it to reverse premultiplication of the RGB channels. To do this in a generic manner that overrides only from-RGBA conversion, I wrote the following custom conversion functor:

// Default to color converters provided by GIL
template<typename TSrcColorSpace, typename TDstColorSpace>
struct ConvertRGBA_impl
: public default_color_converter_impl<TSrcColorSpace, TDstColorSpace> {};

// Override GIL's from-RGBA color converter. GIL's from-RGBA color converter assumes premultiplied alpha, which we do not
// use.
template<typename TDstColorSpace>
struct ConvertRGBA_impl<rgba_t, TDstColorSpace>
{
template<typename TSrcPixel, typename TDstPixel>
void operator () (const TSrcPixel& srcPixel, TDstPixel& dstPixel) const;
};

struct ConvertRGBA
{
template<typename TSrcPixel, typename TDstPixel>
void operator () (const TSrcPixel& srcPixel, TDstPixel& dstPixel) const;
};

template<typename TDstColorSpace>
template<typename TSrcPixel, typename TDstPixel>
void ConvertRGBA_impl<rgba_t, TDstColorSpace>::operator () (const TSrcPixel& srcPixel, TDstPixel& dstPixel) const
{
default_color_converter_impl<rgb_t, TDstColorSpace>()
(
pixel<typename channel_type<TSrcPixel>::type, rgb_layout_t>
(
get_color(srcPixel, red_t()),
get_color(srcPixel, green_t()),
get_color(srcPixel, blue_t())
),
dstPixel
);
}

template<typename TSrcPixel, typename TDstPixel>
void ConvertRGBA::operator () (const TSrcPixel& srcPixel, TDstPixel& dstPixel) const
{
typedef typename color_space_type<TSrcPixel>::type TSrcColorSpace;
typedef typename color_space_type<TDstPixel>::type TDstColorSpace;
ConvertRGBA_impl<TSrcColorSpace, TDstColorSpace>()(srcPixel, dstPixel);
}

This may be used, for example, by calling copy_and_convert_pixels with ConvertRGBA() supplied as the third parameter.

Labels:


Monday, September 6, 2010

 

ATI RV770 (HD4870) OpenCL works on gentoo x86_64

I had the misapprehension that the previous top-end ATI GPU generation does not support OpenCL. Today, I spotted RV770 in the list of GPUs supported by version 2.2 of the ATI stream computing SDK.

My Linux workstation happens to have an RV770-based 512MiB Radeon HD4870. Although my Linux workstation runs Gentoo x86_64 and there is no ebuild for this SDK, installing the SDK was straightforward. Behold, it works:I also came across an OpenCL image convolution routine that I may use in the composite convolution kernel generating, hair finding EA that I am currently developing. I am glad that I'll be able to let the EA run with partial GPU offloading on my Linux workstation. Maxing out the GPU on my windows machine with OpenCL makes screen updates noticeably sluggish, so this is a very nice thing indeed.

Labels: , ,


Monday, December 21, 2009

 

Using Windows Terminal Blocky Font With All Codepage 437 Characters in X11 On Linux and OS X

I must have the Windows terminal default font for SlickEdit and for my console in Linux and on OS X. All codepage 437 characters must be rendered properly. I will not abide a single pixel of deviation from windows terminal rendering.

Andale mono, you say? Andale mono is lithe, anorexic, spindly and illegible. The default cmd.exe font is copyrighted 1984 by Bitstream Inc. It is old, fat, and perfect.

However, neither all the font ebuilds available in Gentoo nor the default fonts included with X-Quartz supply this sturdy, stalwart font. It must be converted from vgaoem.fon to .pcf.gz. To do so, I completed the following steps:

  1. Download and build the latest full Wine sources, omitting nothing
  2. wine-1.1.12/tools/fnt2bdf -c iso10646-1 -f vgaoem -t vgaoem.fon
  3. bdftopcf vgaoem_r400-12.bdf -o vgaoem.pcf
  4. gzip vgaoem.pcf
  5. mkdir fonts
  6. mv vgaoem.pcf.gz fonts/
  7. cd fonts
  8. mkfontdir
  9. xset +fp ~/fonts
  10. xset fp rehash
After successfully completing these steps, executing
 xterm -fn -windows-terminal-medium-r-normal--12-120-96-96-c-80-iso10646-1
, running midnight commander, and using the ctrl-right click xterm menu to enable line-drawing characters produced the following beauty:Here, we see bold characters and line drawing codepage 437 characters rendered correctly with the converted Windows terminal font on Linux. Copying the gz file to OS X and placing it in a new fonts directory within my home provided the same functionality.

In order to verify pixel-perfect fidelity, I screenshotted Visual Slickedit on OS X and Windows and overlayed them in Photoshop. I set the overlay layer to 65% opacity and difference blending mode. Here is part of the image with the layers intentionally offset to demonstrate the effect:And aligned, complete fidelity is verified:Success on all fronts!

Labels: , , ,


Monday, November 16, 2009

 

"The 32-bit version of Visual Studio cannot debug 64-bit processes or 64-bit dumps."

Using Visual Studio 2008 on Vista x64, I encountered this error: "The 32-bit version of Visual Studio cannot debug 64-bit processes or 64-bit dumps."This occurred when I attempted to debug a project that builds an x64 DLL. Specifically, the DLL is a .pyd for a custom build of Python for x64. This .pyd is a boost Python extension built with Visual Studio rather than bjam so as to reduce modify / rebuild / debug cycle time.

Yet, Visual Studio is capable of debugging x64 binaries; I do so often for projects that generate 64-bit executables. I scoured every human readable project and solution file expecting to find a field inaccessible from the GUI that specifies the debugger executable to use for a project and found none. Comparing .suo files in a hex editor revealed no significant differences for projects that work vs those that fail. Finally, I stared for a long time at the project properties debugger pane.Thus, I divined the cause of the problem and a workaround: when Visual Studio resorts to searching the directories in the PATH environment variable for the command to debug, an assumption is made. In this circumstance, Visual Studio assumes that the command will result in execution of a 32-bit binary. When the command is specified as a filename with path, Visual Studio checks the architecture of the specified executable and launches the appropriate debugger, avoiding the problem.

Labels: , ,


Saturday, October 31, 2009

 

Python file renaming script that uses popen to call mv -v,

Note for future reference: as an exercise, I wrote the following file renaming script. I intentionally use popen to call mv -v, the output of which I write to stdout without a redundant line return. I regularly need to do more complex file shuffling operations in Python & need to call to the shell with popen; this example is a handy reference for both.


import re
import os
import subprocess
import sys

rexp = re.compile('^Assignment2and3(.+)$')

for fn in os.listdir('./'):
if os.path.isfile(fn):
reres = rexp.search(fn)
if reres:
cmd = ['mv', '-v', fn, 'Assignment4' + reres.group(1)]
sys.stdout.write( subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] )

Labels: ,


Thursday, September 17, 2009

 

dereferencing a pointer to a member variable taken as an argument

To save myself time in the future, I'm posting a function I wrote that dereferences a pointer to a member variable taken as an argument.


void Parser::loadSymbolSets(SymbolSet Symbol::* symbolSet, const SymbolIdentifier* symbolSetsDef)

{
std::vector<Parser::Symbol*>::iterator symbol(symbols.begin());

const SymbolIdentifier* symbolSetDef(symbolSetsDef);
for(; symbol != symbols.end(); ++symbol)

{
for(;;)
{
if(*symbolSetDef == IdProdSep)

{
++symbolSetDef;
break;
}
if(*symbolSetDef == IdSeqSep)

{
throw std::string("Parser::loadSymbolSets(..): IdSeqSep encountered in symbol set definition array.");
}

((*symbol)->*symbolSet).insert(symbols[*symbolSetDef]);

++symbolSetDef;
}
}
}

Labels:


Saturday, August 29, 2009

 

MS&T CS348 Assignment 1

I wrote a Python application that satisfies the requirements for assignment 1 of a course at Missouri Science and Technology. This is my second time through the class; the first time around, I completed all the programs and none of the papers. Writing the programs is interesting; discursively relating every tedious facet of the theory underlying those programs is not when the only other person to read the work is the grader. According to the university, the English curriculum is so defective that the computer science department must take up the slack. Today's students are functionally illiterate and such Sisyphean tasks do something to fill the student mind with something other than skittles, sex, and rum.

Fortunately, my excellent programs and total disregard for the university's priorities, in combination with my combative sarcasm and vociferous complaints have apparently been rewarded: the course curriculum has been revised to ameliorate the painful, futile demand that so many extraneous reports be composed only to be thrown away. Thus, I am back for a second dose, and it's already quite tasty.

The first assignment requires my program to identify the minimum number of nodes that must be removed from the following graph in order for every node less than 10 to be unreachable from any other node less than 10, except via the reflexive, implied edge. This graph represents the simplest of four supplied data sets, being just 5KiB of text, whereas the largest is 370KiB.


A detail from the graph:


The first assignment required only random search to solve this problem. Nonetheless, I implemented an evolutionary algorithm that performs significantly better. Amusingly, the thing isn't due for ages and can not even be submitted for two more calendar days.

Labels: , , ,


Sunday, July 26, 2009

 

retrieving original iPod music filenames from an iPodDB

I noticed that when iTunes writes a track to an iPod, it hashes the track filename. For example, an MP3 on an iPod might be called HQXA.mp3 and located in one of many directories with two letter numeric names. Incidentally, this style of naming files is similar to that used by CCache, squid, and other applications that store a large number of files.

I desired to import numerous such files from an iPod into iTunes. iTunes supports automatic organization of imported tracks by the information stored in each track (such as artist name, album name, track number, disc number). Thus, for tracks imported into iTunes, filenames are irrelevant - provided that those tracks include tag information. Some of the tracks that I wanted to import included no useful information in their tags. The original tack filenames was descriptive and obviously stored somewhere on the iPod - when the iPod played these tracks, it showed their original filenames (minus extension). I browsed through the data files on the iPod and quickly found that iPod_Control/iTunes/iTunesDB appeared to contain track names in little endian UCS-16 or UTF-16 format in addition to a significant amount of other data.

On a flight from Boston to Reykjavik, I wore down my laptop battery exploring the iTunesDB file from an iPod containing the music I wished to recover. Looking at the file in a hex editor, I quickly recognized that the iTunesDB is definitely a database composed of a hierarchical record structure. Records are delimited by plain ASCII strings beginning with "mh"; offsets and sizes are specified as 32 bit little endian integers that are probably unsigned (I would need a 2GiB+ iTunesDB to verify this - mine was just 500KiB, and the DB does not use virtual addressing).

To keep myself occupied as I overcame jetlag upon reaching Norway, I wrote a ruby module and front end application to retrieve original filenames from an iTunes DB and to give hashed filenames their original names. It worked well with my data; that said, I haven't tested it with any other, so be careful if you use my application.


fix_iPod_filenames.rb (the frontend application)

#! /usr/bin/env ruby19
#
# (c) Erik Hvatum, 2009
#

require 'pathname'
require 'FileUtils'
require 'iTunesDB'

if ARGV.size != 2
abort "Usage: fix_iPod_filenames.rb <directory containing iPod_Control> <directory in which to place renamed music files>"
end

iPodRoot = Pathname.new(File.expand_path(ARGV[0]))
dbPath = iPodRoot + "iPod_Control/iTunes/iTunesDB"
if !dbPath.exist?
abort "iPod database \"#{dbPath}\" does not exist or is inaccessible."
end
destPath = Pathname.new(File.expand_path(ARGV[1]))
if !destPath.exist?
abort "Destination path \"#{destPath}\" does not exist or is inaccessible."
end

db = MiTunesDB::CiTunesDB.new
db.open(dbPath)
tracks = db.tracks
db.close
db = nil

tracks.sort! {|l, r| l.location <=> r.location}
tracks.each do |track|
needSep = false
dstFn = ""
doField = Proc.new do |field|
v = eval "track.#{field}"
if v != nil
if needSep
dstFn << "-"
else
needSep = true
end
dstFn << v.to_s
end
end
doField.call("album")
doField.call("discNumber")
doField.call("trackNumber")
doField.call("artist")
doField.call("title")
dstFn.gsub!(/[*!?\\\/:%]/, "_")
src = iPodRoot.to_s + track.location.gsub(/:/, "/")
dst = destPath.to_s + "/" + dstFn[0, 250] + "." + track.format.downcase.gsub(/ $/, "")
FileUtils.mv(src, dst)
end

iTunesDB.rb (the module used by the frontend application)

# (c) Erik Hvatum, 2009

module MiTunesDB

# Reads the binary DB written by iTunes Windows to an iPod circa late 2008
class CiTunesDB
Track = Struct.new(:title,
:location,
:album,
:artist,
:genre,
:fileType,
:comment,
:composer,
:grouping,
:description,
:albumArtist,
:format,
:trackNumber,
:discNumber)
attr :mhbd
attr :file

def initialize
@mhbd = nil
@file = nil
end

# Opens the iTunesDB specified by filename, loading its contents into memory
def open(fileName)
@file = File.open(fileName, "rb")
@mhbd = Cmhbd.new(self)
end

# Closes the iTunesDB
def close()
@mhbd = nil
@file = nil
end

# Returns all known tracks as an array of Track structures (defined above)
def tracks()
ts = Array.new
if @mhbd
@mhbd.children.each do |mhsd|
if mhsd.children
mhsd.children.each do |sdChild|
if sdChild.is_a? Cmhlt
sdChild.children.each do |mhit|
t = Track.new
ts << t
mhit.children.each do |mhod|
if Cmhod::Types.has_key? mhod.type
sym = (Cmhod::Types[mhod.type].to_s + '=').to_sym
t.method(sym).call(mhod.str)
end
end
t.format = mhit.format
t.trackNumber = mhit.trackNumber
t.discNumber = mhit.discNumber
end
end
end
end
end
end
return ts
end

# Base class for objects in the iTunes DB
class Cmh_base
attr :addr
attr :depth
attr :len
attr :recordName
attr :children

def initialize(addr, db, depth)
@addr = addr
@depth = depth
@len = nil
@db = db
@children = nil
load()
end

def to_s()
indent = "\t" * @depth
return "#{indent}#{@recordName}:\n#{indent} len: #@len\n"
end

protected

def load(loadLen = true)
if @db.file.tell != @addr
@db.file.seek(@addr)
end
recordName = @db.file.read(@recordName.length())
if recordName != @recordName
raise "Invalid record identifier in DB: expected \"#@recordName\" but read \"#{recordName}\"."
end
@len = readUInt32() if loadLen
end

def loadChildren()
@children = []
end

def readUInt32()
return @db.file.read(4).unpack("V")[0]
end

def children_to_s()
ret = String.new
if @children
@children.each { |child| ret << child.to_s() }
end
return ret
end
end

# Record describing the database. This is the first record in the database and is located at the beginning
# of the database file.
class Cmhbd < Cmh_base
attr :dbVersion
attr :dbLen

def initialize(file)
@dbVersion = nil
@dbLen = nil
@recordName = "mhbd"
super(0, file, 0)
end

def to_s()
indent = "\t" * @depth
return super() << "#{indent} dbVersion: #@dbVersion\n#{indent} dbLen: #@dbLen\n" << children_to_s()
end

protected

def load()
super
seekTo = @addr + @recordName.length() + 4
if @db.file.tell != seekTo
@db.file.seek(seekTo)
end
@dbLen = readUInt32()
@dbVersion = Array.new(3) { readUInt32() }
loadChildren()
end

def loadChildren()
super
childAddr = @addr + @len
while childAddr < @dbLen
@children << Cmhsd.new(childAddr, @db, @depth + 1)
childAddr += @children[-1].childSize
end
end
end

# Record describing a dataset.
class Cmhsd < Cmh_base
# Adding childSize to @addr gives the addr of the next mhsd record
attr :childSize
attr :childType

def initialize(addr, db, depth)
@recordName = "mhsd"
@childSize = nil
@childType = nil
super
end

def to_s()
indent = "\t" * @depth
return super() << "#{indent} childSize: #@childSize\n#{indent} childType: #@childType\n" << children_to_s()
end

protected

def load()
super
seekTo = @addr + @recordName.length() + 4
if @db.file.tell != seekTo
@db.file.seek(seekTo)
end
@childSize = readUInt32()
@childType = readUInt32()
loadChildren()
end

def loadChildren()
super
if @childType == 1
@children << Cmhlt.new(@addr + @len, @db, @depth + 1)
end
end
end

# Record describing a track list
class Cmhlt < Cmh_base
attr :numChildren

def initialize(addr, db, depth)
@numChildren = nil
@recordName = "mhlt"
super
end

def to_s()
indent = "\t" * @depth
return super() << "#{indent} numChildren: #@numChildren\n" << children_to_s()
end

protected

def load()
super
seekTo = @addr + @recordName.length() + 4
@db.file.seek(seekTo)
@numChildren = readUInt32()
loadChildren()
end

def loadChildren()
super
addr = @addr + @len
@numChildren.times do
mhit = Cmhit.new(addr, @db, depth + 1)
@children << mhit
addr += mhit.totalLen
end
end
end

# Record describing a track item
class Cmhit < Cmh_base
attr :totalLen
attr :numStrMhods
attr :id
attr :format
attr :trackNumber
attr :discNumber

def initialize(addr, db, depth)
@totalLen = nil
@numStrMhods = nil
@id = nil
@format = nil
@recordName = "mhit"
@trackNumber = nil
@discNumber = nil
super
end

def to_s()
indent = "\t" * @depth
str = super
str << "#{indent} totalLen: #@totalLen\n"
str << "#{indent} numStrMhods: #@numStrMhods\n"
str << "#{indent} id: #@id\n"
str << "#{indent} format: #@format\n"
str << "#{indent} trackNumber: #@trackNumber\n"
str << "#{indent} discNumber: #@discNumber\n"
str << children_to_s()
return str
end

protected

def load()
super
seekTo = @addr + @recordName.length() + 4
@db.file.seek(seekTo)
@totalLen = readUInt32()
@numStrMhods = readUInt32()
@id = readUInt32()
@db.file.seek(4, IO::SEEK_CUR)
@format = @db.file.read(4).reverse()
@db.file.seek(@addr + 44)
trackNumber = readUInt32()
@trackNumber = trackNumber if trackNumber > 0
discNumber = readUInt32()
@discNumber = discNumber if discNumber > 0
loadChildren()
end

def loadChildren()
super
addr = @addr + @len
numStrMhods.times do
mhod = Cmhod.new(addr, @db, depth + 1)
children << mhod
addr += mhod.len
end
end
end

# Record describing a data object
class Cmhod < Cmh_base
Types = {1 => :title,
2 => :location,
3 => :album,
4 => :artist,
5 => :genre,
6 => :fileType,
8 => :comment,
12 => :composer,
13 => :grouping,
14 => :description,
22 => :albumArtist}.freeze
attr :headerLen
attr :type
attr :strLen
attr :str

def initialize(addr, db, depth)
@headerLen = nil
@type = nil
@str = nil
@strLen = nil
@recordName = "mhod"
super
end

def to_s()
indent = "\t" * @depth
str = super
str << "#{indent} headerLen: #@headerLen\n"
str << "#{indent} type: #@type\n"
str << "#{indent} strLen: #@strLen\n"
str << "#{indent} str: #@str\n" if @str
return str
end

protected

def load()
super(false)
@db.file.seek(@addr + @recordName.length())
@headerLen = readUInt32()
@len = readUInt32()
@type = readUInt32()
@db.file.seek(12, IO::SEEK_CUR)
@strLen = readUInt32()
if @strLen > 0 && @strLen % 2 == 0
@db.file.seek(8, IO::SEEK_CUR)
begin
str = @db.file.read(@strLen).force_encoding("UTF-16LE").encode("US-ASCII")
rescue
else
@str = str
end
end
end
end
end

end

Labels: , , ,


Archives

July 2009   August 2009   September 2009   October 2009   November 2009   December 2009   January 2010   September 2010   December 2010   January 2011   February 2011   April 2011   June 2011   August 2011   February 2012   June 2012   July 2012   August 2012   October 2012   November 2012   January 2014   April 2014   June 2014   August 2014   September 2014   October 2014   January 2015   March 2015   April 2015   June 2015   November 2015   December 2015   January 2016   June 2016   August 2016   January 2017   March 2017   April 2018   April 2019   June 2019   January 2020  

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]