gShowBounds = false gUseBlurInTransitions = false gPath = "resources/" function load_file(file) local prev_path = package.path package.path = package.path .. ";" .. gPath .. file .. ".lua" require(file) package.path = prev_path end load_file("slides_utils") gSlides = parse_file(io.open("resources/slides_content2.lua", "r")) function make_rect(l, t, r, b) return { left = l, top = t, right = r, bottom = b } end function make_paint(typefacename, style, size, color) local paint = Sk.newPaint(); paint:setAntiAlias(true) paint:setSubpixelText(true) paint:setTypeface(Sk.newTypeface(typefacename, style)) paint:setTextSize(size) paint:setColor(color) return paint end function draw_bullet(canvas, x, y, paint, indent) if 0 == indent then return end local ps = paint:getTextSize() local cx = x - ps * .8 local cy = y - ps * .4 local radius = ps * .2 canvas:drawCircle(cx, cy, radius, paint) end function stroke_rect(canvas, rect, color) local paint = Sk.newPaint() paint:setStroke(true); paint:setColor(color) canvas:drawRect(rect, paint) end function drawSlide(canvas, slide, master_template) if #slide == 1 then template = master_template.title canvas:drawText(slide[1].text, 320, 240, template[1]) return end template = master_template.slide local x = template.margin_x local y = template.margin_y local scale = 1.25 if slide.blockstyle == "code" then local paint = master_template.codePaint local fm = paint:getFontMetrics() local height = #slide * (fm.descent - fm.ascent) y = (480 - height) / 2 for i = 1, #slide do local node = slide[i] y = y - fm.ascent * scale canvas:drawText(node.text, x, y, paint) y = y + fm.descent * scale end return end 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 * 1.25 local bounds = make_rect(x + x_offset, y, 620, 640) local blob, newBottom = Sk.newTextBlob(node.text, bounds, paint) draw_bullet(canvas, x + x_offset, y - fm.ascent, paint, node.indent) canvas:drawTextBlob(blob, 0, 0, paint) y = newBottom + paint:getTextSize() * .5 + extra_dy if gShowBounds then bounds.bottom = newBottom stroke_rect(canvas, bounds, {a=1,r=0,g=1,b=0}) stroke_rect(canvas, blob:bounds(), {a=1,r=1,g=0,b=0}) end end end -------------------------------------------------------------------------------------- function make_tmpl(paint, extra_dy) return { paint = paint, extra_dy = extra_dy } end function SkiaPoint_make_template() normal = Sk.newFontStyle() bold = Sk.newFontStyle(700) local title = { margin_x = 30, margin_y = 100, } title[1] = make_paint("Arial", bold, 45, { a=1, r=1, g=1, b=1 }) title[1]:setTextAlign("center") title[2] = make_paint("Arial", bold, 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", bold, 35, { a=1, r=1, g=1, b=1 }), 18) slide[2] = make_tmpl(make_paint("Arial", normal, 25, { a=1, r=1, g=1, b=1 }), 10) slide[3] = make_tmpl(make_paint("Arial", normal, 20, { a=1, r=.9, g=.9, b=.9 }), 5) return { title = title, slide = slide, codePaint = make_paint("Courier", normal, 20, { a=1, r=.9, g=.9, b=.9 }), } 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 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 function new_drawable_slide(slide) return { slide = slide, draw = function (self, canvas, x, y, paint) if (nil == paint or ("number" == type(paint) and (1 == paint))) then canvas:save() else canvas:saveLayer(paint) end canvas:translate(x, y) drawSlide(canvas, self.slide, gTemplate) canvas:restore() end } end gNewDrawableFactory = { default = new_drawable_slide, picture = convert_to_picture_drawable, image = convert_to_image_drawable, } ----------------------------------------------------------------------------- function next_slide() local prev = gSlides[gSlideIndex] if gSlideIndex < #gSlides then gSlideIndex = gSlideIndex + 1 spawn_transition(prev, gSlides[gSlideIndex], true) end end function prev_slide() local prev = gSlides[gSlideIndex] if gSlideIndex > 1 then gSlideIndex = gSlideIndex - 1 spawn_transition(prev, gSlides[gSlideIndex], false) end end gDrawableType = "default" load_file("slides_transitions") function spawn_transition(prevSlide, nextSlide, is_forward) local transition if is_forward then transition = gTransitionTable[nextSlide.transition] else transition = gTransitionTable[prevSlide.transition] end if not transition then transition = fade_slide_transition end local prevDrawable = gNewDrawableFactory[gDrawableType](prevSlide) local nextDrawable = gNewDrawableFactory[gDrawableType](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) bgPaint:setDither(true) 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, b = function () gShowBounds = not gShowBounds end, B = function () gUseBlurInTransitions = not gUseBlurInTransitions end, ["1"] = function () gDrawableType = "default" end, ["2"] = function () gDrawableType = "picture" end, ["3"] = function () gDrawableType = "image" end, } function onCharHandler(uni) local proc = keyProcs[uni] if proc then proc() return true end return false end