function tostr(t) local str = "" for k, v in next, t do if #str > 0 then str = str .. ", " end if type(k) == "number" then str = str .. "[" .. k .. "] = " else str = str .. tostring(k) .. " = " end if type(v) == "table" then str = str .. "{ " .. tostr(v) .. " }" elseif type(v) == "string" then str = str .. '"' .. v .. '"' else str = str .. tostring(v) end end return str end function trim_ws(s) return s:match("^%s*(.*)") end function count_hypens(s) local leftover = s:match("^-*(.*)") return string.len(s) - string.len(leftover) end function parse_file(file) local slides = {} local block = {} for line in file:lines() do local s = trim_ws(line) if #s == 0 then -- done with a block if #block > 0 then slides[#slides + 1] = block block = {} end else local n = count_hypens(s) block[#block + 1] = { indent = n, text = trim_ws(s:sub(n + 1, -1)) } end end return slides end function pretty_print_slide(slide) io.write("{\n") for i = 1, #slide do local node = slide[i] for j = 0, node.indent do io.write(" ") end io.write("{ ") io.write(tostr(node)) io.write(" },\n") end io.write("},\n") end function pretty_print_slides(slides) io.write("gSlides = {\n") for i = 1, #slides do pretty_print_slide(slides[i]) end io.write("}\n") end gSlides = parse_file(io.open("/skia/trunk/resources/slides_content.lua", "r")) function make_rect(l, t, r, b) return { left = l, top = t, right = r, bottom = b } end function make_paint(typefacename, stylebits, size, color) local paint = Sk.newPaint(); paint:setAntiAlias(true) paint:setSubpixelText(true) paint:setTypeface(Sk.newTypeface(typefacename, stylebits)) paint:setTextSize(size) paint:setColor(color) return paint end function drawSlide(canvas, slide, template) template = template.slide -- need to sniff the slide to know if we're title or slide local x = template.margin_x local y = template.margin_y local scale = 1.25 for i = 1, #slide do local node = slide[i] local paint = template[node.indent + 1].paint local extra_dy = template[node.indent + 1].extra_dy local fm = paint:getFontMetrics() local x_offset = -fm.ascent * node.indent y = y - fm.ascent * scale canvas:drawText(node.text, x + x_offset, y, paint) y = y + fm.descent * scale + extra_dy end end function scale_text_delta(template, delta) template = template.slide for i = 1, #template do local paint = template[i].paint paint:setTextSize(paint:getTextSize() + delta) end end function slide_transition(prev, next, is_forward) local rec = { proc = function(self, canvas, drawSlideProc) if self:isDone() then drawSlideProc(canvas) return nil end self.prevDrawable:draw(canvas, self.curr_x, 0) self.nextDrawable:draw(canvas, self.curr_x + 640, 0) self.curr_x = self.curr_x + self.step_x return self end } if is_forward then rec.prevDrawable = prev rec.nextDrawable = next rec.curr_x = 0 rec.step_x = -15 rec.isDone = function (self) return self.curr_x <= -640 end else rec.prevDrawable = next rec.nextDrawable = prev rec.curr_x = -640 rec.step_x = 15 rec.isDone = function (self) return self.curr_x >= 0 end end return rec end function sqr(value) return value * value end function set_blur(paint, alpha) local sigma = sqr(1 - alpha) * 20 paint:setImageFilter(Sk.newBlurImageFilter(sigma, sigma)) paint:setAlpha(alpha) end function fade_slide_transition(prev, next, is_forward) local rec = { paint = Sk.newPaint(), prevDrawable = prev, nextDrawable = next, proc = function(self, canvas, drawSlideProc) if self:isDone() then drawSlideProc(canvas) return nil end set_blur(self.paint, self.prev_a) self.prevDrawable:draw(canvas, self.prev_x, 0, self.paint) set_blur(self.paint, self.next_a) self.nextDrawable:draw(canvas, self.next_x, 0, self.paint) self:step() return self end } if is_forward then rec.prev_x = 0 rec.prev_a = 1 rec.next_x = 640 rec.next_a = 0 rec.isDone = function (self) return self.next_x <= 0 end rec.step = function (self) self.next_x = self.next_x - 20 self.next_a = (640 - self.next_x) / 640 self.prev_a = 1 - self.next_a end else rec.prev_x = 0 rec.prev_a = 1 rec.next_x = 0 rec.next_a = 0 rec.isDone = function (self) return self.prev_x >= 640 end rec.step = function (self) self.prev_x = self.prev_x + 20 self.prev_a = (640 - self.prev_x) / 640 self.next_a = 1 - self.prev_a end end return rec end -------------------------------------------------------------------------------------- function make_tmpl(paint, extra_dy) return { paint = paint, extra_dy = extra_dy } end function SkiaPoint_make_template() local title = { margin_x = 30, margin_y = 100, } title[1] = make_paint("Arial", 1, 50, { a=1, r=1, g=1, b=1 }) title[1]:setTextAlign("center") title[2] = make_paint("Arial", 1, 25, { a=1, r=.75, g=.75, b=.75 }) title[2]:setTextAlign("center") local slide = { margin_x = 20, margin_y = 25, } slide[1] = make_tmpl(make_paint("Arial", 1, 36, { a=1, r=1, g=1, b=1 }), 18) slide[2] = make_tmpl(make_paint("Arial", 0, 30, { a=1, r=1, g=1, b=1 }), 0) slide[3] = make_tmpl(make_paint("Arial", 0, 24, { a=1, r=.8, g=.8, b=.8 }), 0) return { title = title, slide = slide, } end gTemplate = SkiaPoint_make_template() gRedPaint = Sk.newPaint() gRedPaint:setAntiAlias(true) gRedPaint:setColor{a=1, r=1, g=0, b=0 } -- animation.proc is passed the canvas before drawing. -- The animation.proc returns itself or another animation (which means keep animating) -- or it returns nil, which stops the animation. -- local gCurrAnimation gSlideIndex = 1 function next_slide() local prev = gSlides[gSlideIndex] gSlideIndex = gSlideIndex + 1 if gSlideIndex > #gSlides then gSlideIndex = 1 end spawn_transition(prev, gSlides[gSlideIndex], true) end function prev_slide() local prev = gSlides[gSlideIndex] gSlideIndex = gSlideIndex - 1 if gSlideIndex < 1 then gSlideIndex = #gSlides end spawn_transition(prev, gSlides[gSlideIndex], false) end function new_drawable_picture(pic) return { picture = pic, width = pic:width(), height = pic:height(), draw = function (self, canvas, x, y, paint) canvas:drawPicture(self.picture, x, y, paint) end } end function new_drawable_image(img) return { image = img, width = img:width(), height = img:height(), draw = function (self, canvas, x, y, paint) canvas:drawImage(self.image, x, y, paint) end } end function convert_to_picture_drawable(slide) local rec = Sk.newPictureRecorder() drawSlide(rec:beginRecording(640, 480), slide, gTemplate) return new_drawable_picture(rec:endRecording()) end function convert_to_image_drawable(slide) local surf = Sk.newRasterSurface(640, 480) drawSlide(surf:getCanvas(), slide, gTemplate) return new_drawable_image(surf:newImageSnapshot()) end gMakeDrawable = convert_to_picture_drawable function spawn_transition(prevSlide, nextSlide, is_forward) local transition if is_forward then transition = prevSlide.transition else transition = nextSlide.transition end if not transition then transition = fade_slide_transition end local prevDrawable = gMakeDrawable(prevSlide) local nextDrawable = gMakeDrawable(nextSlide) gCurrAnimation = transition(prevDrawable, nextDrawable, is_forward) end -------------------------------------------------------------------------------------- function spawn_rotate_animation() gCurrAnimation = { angle = 0, angle_delta = 5, pivot_x = 320, pivot_y = 240, proc = function (self, canvas, drawSlideProc) if self.angle >= 360 then drawSlideProc(canvas) return nil end canvas:translate(self.pivot_x, self.pivot_y) canvas:rotate(self.angle) canvas:translate(-self.pivot_x, -self.pivot_y) drawSlideProc(canvas) self.angle = self.angle + self.angle_delta return self end } end function spawn_scale_animation() gCurrAnimation = { scale = 1, scale_delta = .95, scale_limit = 0.2, pivot_x = 320, pivot_y = 240, proc = function (self, canvas, drawSlideProc) if self.scale < self.scale_limit then self.scale = self.scale_limit self.scale_delta = 1 / self.scale_delta end if self.scale > 1 then drawSlideProc(canvas) return nil end canvas:translate(self.pivot_x, self.pivot_y) canvas:scale(self.scale, self.scale) canvas:translate(-self.pivot_x, -self.pivot_y) drawSlideProc(canvas) self.scale = self.scale * self.scale_delta return self end } end local bgPaint = nil function draw_bg(canvas) if not bgPaint then bgPaint = Sk.newPaint() local grad = Sk.newLinearGradient( 0, 0, { a=1, r=0, g=0, b=.3 }, 640, 480, { a=1, r=0, g=0, b=.8 }) bgPaint:setShader(grad) end canvas:drawPaint(bgPaint) end function onDrawContent(canvas, width, height) local matrix = Sk.newMatrix() matrix:setRectToRect(make_rect(0, 0, 640, 480), make_rect(0, 0, width, height), "center") canvas:concat(matrix) draw_bg(canvas) local drawSlideProc = function(canvas) drawSlide(canvas, gSlides[gSlideIndex], gTemplate) end if gCurrAnimation then gCurrAnimation = gCurrAnimation:proc(canvas, drawSlideProc) return true else drawSlideProc(canvas) return false end end function onClickHandler(x, y) return false end local keyProcs = { n = next_slide, p = prev_slide, r = spawn_rotate_animation, s = spawn_scale_animation, ["="] = function () scale_text_delta(gTemplate, 1) end, ["-"] = function () scale_text_delta(gTemplate, -1) end, } function onCharHandler(uni) local proc = keyProcs[uni] if proc then proc() return true end return false end